微信小程序自定义组件及其数据绑定
1. 自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
1.1 创建自定义组件
类似于页面,一个自定义组件由json,wxml,wxss,js4个文件组成。首先需要在json文件总进行自定义组件声明(将component字段设为true可将这一组文件设为自定义组件) 新建components文件夹,当作共用的组件文件夹,在这个文件夹下新建orderList组件
{ "component": true }复制代码
同时,还要在
wxml
文件中编写组件模板,在wxss
文件中加入组件样式
<!-- 这是自定义组件的内部 WXML 结构 --> <view class="inner"> {{innerText}} </view> <slot></slot>复制代码
/* 这里的样式只应用于这个自定义组件 */ .inner { color: red; }复制代码
在自定义组件的
js
文件中,需要使用Component()
来注册组件,并提供组件的属性定义、内部数据和自定义方法。组件的属性值和内部数据将被用于组件wxml
的渲染,其中,属性值是可由组件外部传入的。
Component({ properties: { // 这里定义了 innerText 属性,属性值可以在组件使用时指定 innerText: { type: String, value: 'default value', } }, data: { // 这里是一些组件内部数据 someData: {} }, methods: { // 这里是一个自定义方法 customMethod: function(){} } })复制代码
使用已注册的自定义组件前,首先要在页面的
json
文件中进行引用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径:
{ "usingComponents": { "orderList": "/components/orderList/orderList" } }复制代码
在页面的
wxml
中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。
<!-- 使用组件 --> <orderList inner-text="sb"></orderList>复制代码
1.2 组件模板和样式
类似于页面,自定义组件拥有自己的wxml模板和wxss样式
1.2.1 组件模板
组件模板与组件数据结合后生成的节点树,将被插入到组件的引用位置上。在组件模板中可以提供一个
<slot>
节点,用于承载组件引用时提供的子节点。
<!-- 组件模板 --> <view class="wrapper"> <view>这里是组件的内部节点</view> <slot></slot> </view>复制代码
<!-- 引用组件的页面模板 --> <view> <!-- 使用组件 --> <orderList inner-text="sb"> <!-- 这部分内容将被放置在组件<slot>的位置上 --> <view>这里是插入到组件slot中的内容</view> </orderList> </view>复制代码
1.2.2 模板数据绑定
与普通的 WXML 模板类似,可以使用数据绑定,这样就可以向子组件的属性传递动态数据
组件的属性
propA
和propB
将收到页面传递的数据。页面可以通过setData
来改变绑定的数据字段
<!-- 引用组件的页面模板 --> <view> <orderList prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}"> <!-- 这部分内容将被放置在组件 <slot> 的位置上 --> <view>这里是插入到组件 slot 中的内容</view> </orderList> </view>复制代码
1.2.3 组件wxml的slot
在组件的 wxml 中可以包含
slot
节点,用于承载组件使用者提供的 wxml 结构。默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。
Component({ options: { multipleSlots: true // 在组件定义时的选项中启用多 slot 支持 }, properties: { /* ... */ }, methods: { /* ... */ } })复制代码
此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的
name
来区分。
<!-- 组件模板 --> <view class="wrapper"> <slot name="before"></slot> <view>这里是组件的内部细节</view> <slot name="after"></slot> </view>复制代码
使用时,用
slot
属性来将节点插入到不同的 slot 上。
<!-- 引用组件的页面模板 --> <view> <component-tag-name> <!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 --> <view slot="before">这里是插入到组件slot name="before"中的内容</view> <!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 --> <view slot="after">这里是插入到组件slot name="after"中的内容</view> </component-tag-name> </view>复制代码
1.2.4 组件的样式隔离
默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:
app.wxss
或页面的wxss
中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。指定特殊的样式隔离选项
styleIsolation
。
Component({ properties:{}, data:{}, methods:{}, options:{ styleIsolation: "isolated" } })复制代码
styleIsolation
选项从基础库版本 [2.6.5]开始支持。它支持以下取值:
isolated
表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);apply-shared
表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;shared
表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了apply-shared
或shared
的自定义组件。(这个选项在插件中不可用。)
1.3 Componnet构造器
Component
构造器可用于定义组件,调用 Component
构造器时可以指定组件的属性、数据、方法等。
Component({ behaviors: [], properties: { myProperty: { // 属性名 type: String, value: '' }, myProperty2: String // 简化的定义方式 }, data: {}, // 私有数据,可用于模板渲染 lifetimes: { // 生命周期函数,可以为函数,或一个在 methods 段中定义的方法名 attached: function () { }, moved: function () { }, detached: function () { }, }, // 生命周期函数,可以为函数,或一个在 methods 段中定义的方法名 attached: function () { }, // 此处 attached 的声明会被 lifetimes 字段中的声明覆盖 ready: function() { }, pageLifetimes: { // 组件所在页面的生命周期函数 show: function () { }, hide: function () { }, resize: function () { }, }, methods: { onMyButtonTap: function(){ this.setData({ // 更新属性和数据的方法与更新页面数据的方法类似 }) }, // 内部方法建议以下划线开头 _myPrivateMethod: function(){ // 这里将 data.A[0].B 设为 'myPrivateData' this.setData({ 'A[0].B': 'myPrivateData' }) }, _propertyChange: function(newVal, oldVal) { } } )}复制代码
1.4 组件间通信
组件间的基本通信方式有以下几种
WXML 数据绑定:用于父组件向子组件的指定属性设置数据
事件:用于子组件向父组件传递数据,可以传递任意数据。
父组件还可以通过
this.selectComponent
方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
1.4.1 触发事件
自定义组件触发事件时,需要使用triggerEvent方法,指定事件名、detail对象和事件选项
<!-- 在自定义组件中 --><button bindtap="onTap"></button>复制代码
Component({ properties:{}, method:{ onTap(){ this.triggerEvent('myevent',{}) } } })复制代码
1.4.2 监听事件
事件系统是组件间通信的主要方式之一。自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件。
<!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 --><event bindmyevent="onMyEvent" /><!-- 或者可以写成 --><event bind:myevent="onMyEvent" />复制代码
Page({ onMyEvent: function(e){ e.detail // 自定义组件触发事件时提供的 detail 对象 } })复制代码
1.4.3 获取组件实例
可以在组件里调用this.selectComponent,获取子组件的实例对象。调用时需要传入一个匹配选择器selector.注意 :默认情况下,小程序与插件之间、不同插件之间的组件将无法通过
selectComponent
得到组件实例(将返回null
)。
// 父组件 Page({ data: {}, getChildComponent: function () { const child = this.selectComponent('.my-component'); console.log(child) } })复制代码
1.5 组件生命周期
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。最重要的生命周期是created,attached,detached,包含一个组件实例生命流程的最主要时间点。
组件实例刚刚被创建好时,
created
生命周期被触发。此时,组件数据this.data
就是在Component
构造器中定义的数据data
。 此时还不能调用setData
。 通常情况下,这个生命周期只应该用于给组件this
添加一些自定义属性字段。在组件完全初始化完毕、进入页面节点树后,
attached
生命周期被触发。此时,this.data
已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。在组件离开页面节点树后,
detached
生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则detached
会被触发。
Component({ properties:{}, behaviors:[], data{}, methods:{}, options:{}, lifetimes:{ created(){ console.log("组件刚刚被创建好,此时还不能调用setData"); }, attached(){ console.log("组件初始化完毕"); }, detached(){ console.log("组件离开页面节点树后,detached生命周期被触发"); } } })复制代码
1.5.1 定义生命周期方法
生命周期方法可以直接定义在Component构造器的第一级参数中,也可以在
lifetimes
字段内进行声明(优先级最高)在 behaviors 中也可以编写生命周期方法,同时不会与其他 behaviors 中的同名生命周期相互覆盖。但要注意,如果一个组件多次直接或间接引用同一个 behavior ,这个 behavior 中的生命周期函数在一个执行时机内只会执行一次。
生命周期 参数 描述 created 无 在组件实例刚刚被创建时执行 attached 无 在组件实例进入页面节点树时执行 ready 无 在组件在视图层布局完成后执行 moved 无 在组件实例被移动到节点树另一个位置时执行 detached 无 在组件实例被从页面节点树移除时执行 error Object Error
每当组件方法抛出错误时执行
1.5.2 组件所在页面的生命周期
还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在
pageLifetimes
定义段中定义。生命周期 参数 描述 show 无 组件所在的页面被展示时执行 hide 无 组件所在的页面被隐藏时执行 resize Object Size
组件所在的页面尺寸变化时执行
Component({ pageLifetimes: { show: function() { // 页面被展示 }, hide: function() { // 页面被隐藏 }, resize: function(size) { // 页面尺寸变化 } } })复制代码
1.6 behaviors
behaviors是用于组件间代码共享的特性,类似于vue中的mixins。每个behavior可以包含一组属性、数据、声明周期函数和方法。组件引用它时,它的属性、数据和方法会合并到组件中,生命周期函数也会在对应时机被调用。每个组件可以引用多个
behavior
,behavior
也可以引用其它behavior
。
// my-component.js 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') }, } })复制代码
2.同名字段的覆盖和组合规则
如果有同名的属性 (properties) 或方法 (methods):
若组件本身有这个属性或方法,则组件的属性或方法会覆盖
behavior
中的同名属性或方法;若组件本身无这个属性或方法,则在组件的
behaviors
字段中定义靠后的behavior
的属性或方法会覆盖靠前的同名属性或方法;
如果有同名的数据字段(data):若同名的数据字段都是对象类型,会进行对象合并.
生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用