微信小程序自定义组件
组件类似于页面,一个自定义组件由 json wxml wxss js 4个文件组成。要编写一个自定义组件,需要在json文件中component字段设为true。
简单案例
1.components/my-components.json
1 2 3 4
| { "component": true, "usingComponents": {} }
|
2.components/my-components.wxml
1 2 3 4
| <view class="inner"> {{innerText}} </view> <slot></slot>
|
3.components/my-components.wxss
4.components/my-components.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Component({ properties: { innerText: { type: String, value: 'default value', } }, data: { someData: {} }, methods: { customMethod: function () { } } })
|
5.在页面使用(index/index.json)
1 2 3 4 5
| { "usingComponents": { "my-component": "/components/my-component" } }
|
1 2 3
| <view> <component-tag-name inner-text="Some text"></component-tag-name> </view>
|
6.注意事项
- 在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
- 自定义组件的标签名,只能是小写字母、中划线和下划线的组合
- 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用 usingComponents 字段)
- 自定义组件和页面所在项目根目录名不能以“wx-”为前缀,否则会报错。
组件中的slot
1.默认情况下,组件中只能只有一个slot。需要使用多solt时,需要在js组件中声明启用
1 2 3 4 5 6
| Component({ options: { multipleSlots: true } })
|
组件中存在多个slot,需要name进行区分
组件内
1 2 3 4 5
| <view class="wrapper"> <slot name="before"></slot> <view>这里是组件的内部细节</view> <slot name="after"></slot> </view>
|
使用组件
1 2 3 4 5 6
| <view> <my-component> <view slot="header">Header Content</view> <view slot="footer">Footer Content</view> </my-component> </view>
|
2.组样式隔离
默认情况下,自定义组件的样式只受自定义组件wxss。除非一下俩种情况。
1 2 3
| options: { styleIsolation: 'isolated' }
|
- isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
- apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
- shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。
3.外部样式
有时,组件希望接受外部传入的样式类
1 2 3 4
| Component({ externalClasses: ['my-class'] })
|
1 2
| <custom-component class="my-class">这段文本的颜色由组件外的 class 决定</custom-component>
|
1 2 3 4
| .my-class{ color: red; }
|
4.虚拟化组件节点
默认情况下,自定义组件本身的那个节点是一个“普通”的节点,使用时可以在这个节点上设置 class style 、动画、 flex 布局等,就如同普通的 view 组件节点一样。
1 2 3 4 5
| <view style="display: flex"> <custom-component style="color: blue; flex: 1">蓝色、满宽的</custom-component> </view>
|
但是自定义组件不希望本身可以设置样式,这种情况下,可以将这个自定义组件设置“虚拟的”
1 2 3 4 5 6 7 8 9 10 11 12
| Component({ options: { virtualHost: true }, properties: { style: { type: String, } }, externalClasses: ['class'], })
|
可以将 flex 放入自定义组件内:
1 2 3 4 5
| <view style="display: flex"> <custom-component style="color: blue">不是蓝色的</custom-component> </view>
|
组件内
1 2 3 4
| <view style="flex: 1"> 满宽的 <slot></slot> </view>
|
Component构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Component({ properties: { innerText: { type: String, value: 'default value', } myProperty2: String }, data: {}, options: { multipleSlots: true, virtualHost: true, styleIsolation: 'isolated', }, externalClasses: ['my-class'], })
|
组件通信和事件
组件间的通信
- 父组件向子组件通信(properties)
- 子组件向父组件传递数据,可以传递任意数据(事件)
- 父组件可以通过this.selectComponent()获取子组件实例,从而调用子组件的方法。
监听事件
1 2 3 4
| <component-tag-name bindmyevent="onMyEvent" />
<component-tag-name bind:myevent="onMyEvent" />
|
1 2 3 4 5
| Page({ onMyEvent: function(e){ e.detail } })
|
触发事件
1
| <button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>
|
1 2 3 4 5 6 7 8 9 10
| Component({ properties: {}, methods: { onTap: function(){ var myEventDetail = {} var myEventOption = {} this.triggerEvent('myevent', myEventDetail, myEventOption) } } })
|
自定义的组件实例获取结果
若需要自定义 selectComponent 返回的数据,可使用内置 behavior: wx://component-export
1 2 3 4 5 6 7
| Component({ behaviors: ['wx://component-export'], export() { return { myField: 'myValue' } } })
|
1 2
| <my-component id="the-id" />
|
1 2 3
| <!-- 使用自定义组件时 -->
const child = this.selectComponent('#the-id')
|
组件生命周期
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。
create 组件数据 this.data 就是在 Component 构造器中定义的数据 data 。 此时还不能调用 setData
attached this.data 已被初始化为组件的当前值。
detached,组件离开页面节点后,该生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。
定义生命周期的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Component({ lifetimes: { attached: function() { }, detached: function() { }, }, attached: function() { }, detached: function() { }, })
|
生命周期 |
参数 |
描述 |
created |
无 |
在组件实例刚刚被创建时执行 |
attached |
无 |
在组件实例进入页面节点树时执行 |
ready |
无 |
在组件在视图层布局完成后执行 |
moved |
无 |
在组件实例被移动到节点树另一个位置时执行 |
detached |
无 |
在组件实例被从页面节点树移除时执行 |
error |
无 |
每当组件方法抛出错误时执行 |
组件所在页面的生命周期
生命周期 |
参数 |
描述 |
show |
无 |
组件所在的页面被展示时执行 |
hide |
无 |
组件所在的页面被隐藏时执行 |
resize |
Object Size |
组件所在的页面尺寸变化时执行 |
routeDone |
无 |
组件所在页面路由动画完成时执行 |
1 2 3 4 5 6 7 8 9 10 11 12 13
| Component({ pageLifetimes: { show: function() { }, hide: function() { }, resize: function(size) { } } })
|
自定义 tabBar 的 pageLifetime 不会触发。
behaviors
每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior ,behavior 也可以引用其它 behavior 。
1 2 3
| //index.wxml <my-component my-behavior-property="behavior-property" my-property="my-property"> </my-component>
|
1 2 3
| //component.wxml <button bindtap="myBehaviorMethod">点击触发 behavior 方法</button> <button bindtap="myMethod">点击触发 component 方法</button>
|
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
| module.exports = Behavior({ behaviors: [], properties: { myBehaviorProperty: { type: String } }, data: { myBehaviorData: 'my-behavior-data' }, created: function () { console.log('[my-behavior] created') }, attached: function () { console.log('[my-behavior] attached') }, ready: function () { console.log('[my-behavior] ready') },
methods: { myBehaviorMethod: function () { console.log('[my-behavior] log by myBehaviorMehtod') }, } })
|
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
| var myBehavior = require('my-behavior') Component({ behaviors: [myBehavior], properties: { myProperty: { type: String } }, data: { myData: 'my-component-data' }, created: function () { console.log('[my-component] created') }, attached: function () { console.log('[my-component] attached') }, ready: function () { console.log('[my-component] ready') }, methods: { myMethod: function () { console.log('[my-component] log by myMethod') }, } })
|
组件间的关系
1 2 3 4
| <custom-ul> <custom-li> item 1 </custom-li> <custom-li> item 2 </custom-li> </custom-ul>
|
custom-ul 和 custom-li 都是自定义组件,它们有相互间的关系,微信提供了 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
| Component({ relations: { './custom-li': { type: 'child', linked: function(target) { }, linkChanged: function(target) { }, unlinked: function(target) { } } }, methods: { _getAllLi: function(){ var nodes = this.getRelationNodes('path/to/custom-li') } }, ready: function(){ this._getAllLi() } })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Component({ relations: { './custom-ul': { type: 'parent', linked: function(target) { }, linkChanged: function(target) { }, unlinked: function(target) { } } } })
|
先执行ul种的linked,后执行li的linked
注意:必须在两个组件定义中都加入relations定义,否则不会生效。
具体参数