使用方法

       与页面类似,一个自定义组件也由 json、wxml、wxss、js 四个文件组成。编写自定义组件需要在 json 文件中声明:

1
2
3
{
   "component": true
}

       在页面中使用自定义组件之前,要在页面的 json 文件中进行引用声明:

1
2
3
4
5
6
{
   "usingComponents": {
       // 自定义标签名只能包含小写字母,中划线和下划线
       "component-tag-name": "page/to/custom/component"
  }
}

       然后就可以使用自定义组件:

1
2
3
<view>
 <component-tag-name inner-text="Some text"></component-tag-name>
</view>

组件模板和样式

       可以向组件传递节点,用于承载需要组件引用的子节点。需要使用多个 slot 时需要在组件的 js 中声明,用 name 区分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//组件 js
Component({
   options: {
       multipleSlots: true
  }
})

//组件 wxml
<view class="wrapper">
 <slot name="before"></slot>
 <view>这里是组件的内部细节</view>
 <slot name="after"></slot>
</view>

//页面 wxml
<view>
 // 可以使用数据绑定向子组件的属性传递动态数据
 <component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}">
   <view slot="before">这里是插入到组件 slot name="before"中的内容</view>
   <view slot="after">这里是插入到组件 slot name="after"中的内容</view>
 </component-tag-name>
</view>

       组件对应的 wxss 文件的样式只对组件 wxml 内的节点生效,在组件内不能使用 id、属性、标签、后代选择器,子元素选择器>只能用于 view 组件和其子节点。可以使用:host 指定所在节点的默认样式。
       在自定义组件激活 addGlobalClass 选项可以让自定义组件能被 app.wxss 或页面的 wxss 中的所有样式影响。
       在 Component 中可以用 externalClasses 定义若干个外部样式类,样式类是写在页面中的。

1
2
3
4
5
6
7
8
9
10
11
12
// 页面 wxml
<custom-component my-class="red-text" />

// 组件 wxml    
<custom-component class="my-class">
  这段文本的颜色由组件外的 class 决定
</custom-component>

// 组件 js
Component({
 externalClasses: ['my-class']
})

Component 构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Component({
 // 用于组件间的代码共享,可以使用 Behavior()构造器定义,每个 behavior 包含一组属性、数据、声明周期和方法,被组件引用时,这些东西会被合并到组件中去
 behaviors: [],

 properties: {
   // 如果用 Component 构造页面,在 properties 中可以接收到页面参数
   myProperty: { // 属性名
     type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)

     value: '', // 属性初始值(可选),如果未指定则会根据类型选择一个

     observer(newVal, oldVal, changedPath) {
       // 属性被改变时执行的函数(可选),也可以写成在 methods 段中定义的方法名字符串, 如:'\_propertyChange'
       // 通常 newVal 就是新设置的数据, oldVal 是旧数据
    }
  },
   myProperty2: String // 简化的定义方式
},

 data: {}, // 私有数据,可用于模板渲染

 lifetimes: {
   // 生命周期函数,可以为函数,或一个在 methods 段中定义的方法名
   attached() { }, // 在组件实例进入页面节点树时

   moved() { }, // 在组件实例被移动到节点树另一个位置时执行

   detached() { }, // 在组件实例从页面节点树移除时执行

   // 还有 created、ready、error 生命周期
},

 pageLifetimes: {
   // 组件所在页面的生命周期函数

   show() { }, // 页面被展示时执行

   hide() { }, // 页面被隐藏时执行

   resize() { }, //页面尺寸变化时执行
},

 methods: {
   onMyButtonTap() {
     this.setData({
       // 更新属性和数据的方法与更新页面数据的方法类似
    })
  },

   // 内部方法建议以下划线开头
   _myPrivateMethod() {
     // 这里将 data.A[0].B 设为 'myPrivateData'
     this.setData({
       'A[0].B': 'myPrivateData'
    })
  },

   _propertyChange(newVal, oldVal) {
  }
}
})

       组件中包含一些通用属性和方法,可以通过 this 访问:
组件的通用属性和方法

组件间通信、事件与关系

组件间通信

       父 -> 子:wxml 数据绑定通过设置属性传递数据,或通过 this.selectComponent 方法获取子组件实例对象
       子 -> 父:通过事件传递数据

监听/触发事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 监听事件
// 父组件 wxml
<component-tag-name bind:myevent="onMyEvent" />

// 父组件 js
Page({
 onMyEvent(e) {
   e.detail // 自定义组件触发事件时提供的 detail 对象
}
})

// 触发事件
// 子组件 wxml
<button bind:tap="onTap">点击这个按钮将触发“myevent”事件</button>

// 子组件 js
Component({
 properties: {},

 methods: {
   onTap() {
     const myEventDetail = {} // detail 对象,提供给事件监听函数

     // 触发事件的选项
     const myEventOption = {
         bubbles: Boolean,    // 表示事件是否冒泡,默认为 false
         composed: Boolean,    // 表示事件是否可以穿越组件边界,默认为 false
         capturePhase: Boolean,    //表示事件是否有捕获阶段,默认为 false
    }

     this.triggerEvent('myevent', myEventDetail, myEventOption)
  }
}
})

组件间关系

       如果两个自定义组件有嵌套关系,相互间的通信比较复杂,可以加入 relations 定义段。需要在两个组件定义中都加入 relations 定义,否则不会生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 组件 wxml
<custom-ul>
 <custom-li>item 1</custom-li>
 <custom-li>item 2</custom-li>
</custom-ul>

// 外层组件 js
Component({
 relations: {
   './custom-li': {
     type: 'child', // 关联的目标节点应为子节点

     linked(target) {
       // 每次有 custom-li 被插入时执行,target 是该节点实例对象,触发在该节点 attached 生命周期之后
    },

     linkChanged(target) {
       // 每次有 custom-li 被移动后执行,target 是该节点实例对象,触发在该节点 moved 生命周期之后
    },

     unlinked(target) {
       // 每次有 custom-li 被移除时执行,target 是该节点实例对象,触发在该节点 detached 生命周期之后
    }
  }
},

 methods: {
   _getAllLi() {
     // 使用 getRelationNodes 可以获得 nodes 数组,包含所有已关联的 custom-li,且是有序的
     const nodes = this.getRelationNodes('path/to/custom-li')
  }
},

 ready() {
   this._getAllLi()
}
})

       内层组件写法类似,type 为 parent,linked / linkChanged / unlinked 代表被插入/移动/移除时执行。
       如果外层组件想要关联两个内层组件,并且如果两个组件都有一个 behavior:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 组件 wxml
<custom-form>
 <view>
   input
   <custom-input></custom-input>
 </view>
 <custom-submit>submit</custom-submit>
</custom-form>

// 外层组件 js
const customFormControls = require('./custom-form-controls')
Component({
 relations: {
   customFormControls: {
     type: 'descendant', // 关联的目标节点应为子孙节点
     target: customFormControls
  }
}
})

// 内层组件 js
const customFormControls = require('./custom-form-controls')
Component({
 behaviors: [customFormControls],

 relations: {
   './custom-form': {
     type: 'ancestor', // 关联的目标节点应为祖先节点
  }
}
})

抽象节点

       当自定义组件模板中的节点由调用者决定时,就可以把节点声明为抽象节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 一个选框组组件,可以放置单选框或者复选框
// 组件 wxml
<view wx:for="{{labels}}">
 <label>
   <selectable disabled="{{false}}"></selectable>
  {{item}}
 </label>
</view>

// 组件 json
{
 "componentGenerics": {
   "selectable": true
   // 可以指定一个默认组件
   //"selectable": {
   // "default": "path/to/default/component"
   //}
}
}

// 调用组件的 wxml
<selectable-group generic:selectable="custom-radio" />
<selectable-group generic:selectable="custom-checkbox" />

// 调用组件的 json,有可能调用的组件都需要包含在 json 的 usingComponent 中
{
 "usingComponents": {
   "custom-radio": "path/to/custom/radio",
   "custom-checkbox": "path/to/custom/checkbox"
}
}