《十一》微信小程序中自定义组件的 Component()
Behavior():注册一个 behavior,接受一个 Object 类型的参数。// 定义 behavior// 引入的其它 behavior// 属性},// 数据data: {},// 数据字段监听器// 生命周期声明对象// 所在页面的生命周期声明对象// 方法methods: {})自定义组件扩展其实就是提供了修改自定义组件定义段的能力。Behavior() 构造器提供了新的定义段
Component()
: 用来创建自定义组件。接受一个 Object 类型的参数,可以指定组件的属性、数据、方法等。
-
使用 Component 构造器构造组件:
Component({ // 配置选项 options: {}, // 传入的数据:在 properties 定义段中,属性名采用驼峰写法(propertyName);在 wxml 中,指定属性值时则对应使用连字符写法(property-name=""),应用于数据绑定时采用驼峰写法(attr="propertyName") properties: { myProperty: { type: String, value: '' }, myProperty2: String // 简化的定义方式 }, // 内部的数据 data: {}, // 类似于 mixins 和 traits 的组件间代码复用机制 behaviors: [], // 数据监听器,用于监听 properties 和 data 的变化 observers:{}, // 组件间关系定义 relations: {}, // 外部样式类 externalClasses: [], // 组件生命周期声明对象 lifetimes: { attached: function () { }, moved: function () { }, detached: function () { }, }, // 组件所在页面的生命周期声明对象 pageLifetimes: { show: function () { }, hide: function () { }, resize: function () { }, }, // 方法,包括事件响应函数和任意的自定义方法 methods: { onMyButtonTap: function(){}, // 内部方法建议以下划线开头 _myPrivateMethod: function(){}, }, })
-
使用 Component 构造器构造页面:小程序的页面也可以视为自定义组件,因此页面也可以使用 Component 构造器构造,拥有与普通组件一样的定义段与实例方法。
使用 Component 构造器来构造页面的一个好处是可以使用 behaviors 来提取所有页面中公用的代码段。
此时:
- 要求对应 json 文件中包含 usingComponents 定义段。
{ "usingComponents": {} }
- 页面的生命周期方法(即 on 开头的方法),应写在 methods 定义段中。
Component({ methods: { onLoad: function() {} } })
- 组件的属性可以用于接收页面的参数,如访问页面
/pages/index/index?paramA=123¶mB=xyz
,如果声明有属性 paramA 或 paramB ,则它们会被赋值为 123 或 xyz 。Component({ properties: { paramA: Number, paramB: String, }, methods: { onLoad: function() { this.data.paramA // 页面参数 paramA 的值 this.data.paramB // 页面参数 paramB 的值 } } })
- 要求对应 json 文件中包含 usingComponents 定义段。
生命周期:
组件的生命周期:
组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的事件时被自动触发。可以直接定义在 Component 构造器的第一级参数中;也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)。
组件的生命周期函数:
- created:在组件实例刚刚被创建时执行。
此时还不能调用 setData 。
- attached: 在组件实例进入页面节点树时执行。
绝大多数初始化工作可以在这个生命周期进行。
- ready:在组件在视图层布局完成后执行。
- moved:在组件实例被移动到节点树另一个位置时执行。
- detached:在组件实例被从页面节点树移除时执行。
- error:每当组件方法抛出错误时执行。
Component({
lifetimes: {
attached: function() {},
detached: function() {},
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {},
detached: function() {},
})
组件所在页面的生命周期:
组件所在页面的生命周期在 pageLifetimes 定义段中定义。
- show:组件所在的页面被展示时执行。
- hide:组件所在的页面被隐藏时执行。
- resize:组件所在的页面尺寸变化时执行。
Component({
// 组件所在页面的生命周期声明对象
pageLifetimes: {
// 组件所在的页面被展示时执行
show: function () { },
},
})
自定义组件和页面生命周期函数的执行顺序:
页面引用自定义组件,初次加载的生命周期函数执行顺序:
behaviors:
behaviors 是用于组件间代码共享的特性,类似于一些框架中的 mixins 混入。
每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。
每个组件可以引用多个 behavior ,behavior 也可以引用其它 behavior 。
定义 behavior:
Behavior()
:注册一个 behavior,接受一个 Object 类型的参数。
// 定义 behavior
// my-behavior.js
export default Behavior({
// 引入的其它 behavior
behaviors: [],
// 属性
properties: {
myBehaviorProperty: {
type: String
}
},
// 数据
data: {
myBehaviorData: {}
},
// 数据字段监听器
observers: {},
// 生命周期声明对象
lifetimes: {},
// 所在页面的生命周期声明对象
pageLifetimes: {}.
// 方法
methods: {
myBehaviorMethod: function(){}
}
})
使用 behavior:
组件引用时,在 behaviors 定义段中将它们逐个列出即可。
Page 中不能使用 behaviors。
// 组件中使用 behavior
// my-component.js
import myBehavior from 'my-behavior'
Component({
behaviors: [myBehavior],
})
同名字段的覆盖和组合原则:
组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:
- 如果有同名的属性 properties 或方法 methods:
- 若组件本身有这个属性或方法,则组件的属性或方法会覆盖 behavior 中的同名属性或方法;
- 若组件本身无这个属性或方法,则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法;
- 在 2 的基础上,若存在嵌套引用 behavior 的情况,则规则为:父 behavior 覆盖子 behavior 中的同名属性或方法;
- 如果有同名的数据字段 data:
- 若同名的数据字段都是对象类型,会进行对象合并;
- 其余情况会进行数据覆盖,覆盖规则为:组件 > 父 behavior > 子 behavior 、 靠后的 behavior > 靠前的 behavior。(优先级高的覆盖优先级低的,最大的为优先级最高);
- 生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用:
- 对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序;
- 对于同种生命周期函数,遵循如下规则:behavior 优先于组件执行、子 behavior 优先于 父 behavior 执行、靠前的 behavior 优先于靠后的 behavior 执行;
- 如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次;
内置 behaviors:
自定义组件可以通过引用内置的 behavior 来获得内置组件的一些行为。
内置 behavior 往往会为组件添加一些属性。
在没有特殊说明时,组件可以覆盖这些属性来改变它的 type 或添加 observer 。
wx://form-field
:使自定义组件有类似于表单控件的行为。 form 组件可以识别这些自定义组件,并在 submit 事件中返回组件的字段名及其对应字段值。wx://form-field-group
:使 form 组件可以识别到这个自定义组件内部的所有表单控件。wx://form-field-button
:使 form 组件可以识别到这个自定义组件内部的 button 。如果自定义组件内部有设置了 form-type 的 button ,它将被组件外的 form 接受。wx://component-export
:使自定义组件支持 export 定义段。这个定义段可以用于指定组件被 selectComponent 调用时的返回值。
Component({
behaviors: ['wx://form-field']
})
数据监听器 observers:
数据监听器可以用于监听和响应任何属性和数据字段的变化。
数据监听器监听的是 setData 涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依然会被触发。
如果在数据监听器函数中使用 setData 设置本身监听的数据字段,可能会导致死循环。
例如, this.data.sum 永远是 this.data.numberA 与 this.data.numberB 的和。此时,可以使用数据监听器进行如下实现:
Component({
observers: {
'numberA, numberB': function(numberA, numberB) {
// 在 numberA 或者 numberB 被设置时,执行这个函数
this.setData({
sum: numberA + numberB
})
}
}
})
数据监听器支持监听属性或内部数据的变化,可以同时监听多个。一次 setData 最多触发每个监听器一次。
Component({
observers: {
'some.subfield': function(subfield) {
// 使用 setData 设置 this.data.some.subfield 时触发;除此以外,使用 setData 设置 this.data.some 也会触发
subfield === this.data.some.subfield
},
'arr[12]': function(arr12) {
// 使用 setData 设置 this.data.arr[12] 时触发;除此以外,使用 setData 设置 this.data.arr 也会触发
arr12 === this.data.arr[12]
},
}
})
如果需要监听所有子数据字段的变化,可以使用通配符 **
。
Component({
observers: {
'some.field.**': function(field) {
// 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发;除此以外,使用 setData 设置 this.data.some 也会触发
field === this.data.some.field
},
}
})
仅使用通配符 **
可以监听全部 setData 。
Component({
observers: {
'**': function() {
// 每次 setData 都触发
},
}
})
组件间关系 relations:
当两个自定义组件之间存在嵌套关系时,可以在两个组件中定义 relations,从而可以直接访问对方组件的实例对象。
必须在两个组件中都定义 relations,否则不会生效。
relations 定义段包含目标组件路径及其对应选项。
选项:
- type:目标组件的相对关系,可选的值为 parent 、 child 、 ancestor 、 descendant。
- linked:关系生命周期函数,当关系被建立在页面节点树中时触发,触发时机在组件 attached 生命周期之后。
- linkChanged:关系生命周期函数,当关系在页面节点树中发生改变时触发,触发时机在组件 moved 生命周期之后。
- unlinked:关系生命周期函数,当关系脱离页面节点树时触发,触发时机在组件 detached 生命周期之后。
- target:如果这一项被设置,则它表示关联的目标节点所应具有的 behavior,所有拥有这一 behavior 的组件节点都会被关联。
定义组件间关系后,可以通过 getRelationNodes(path)
方法获取到路径对应的组件实例组成的数组。通过该方法,可以直接获取到关联组件的 properties 属性、data 数据等,并且能够调用关联组件中的方法,修改关联组件实例的变量。
然后可以通过对应组件的 setData() 方法,修改组件实例的变量
当关联组件不止一个的时候,数组会包含所有的组件实例,并按照文档流的顺序排列。
例如:custom-ul 和 custom-li 都是自定义组件,它们存在嵌套关系。
<!-- index.wxml -->
<!-- custom-ul 和 custom-li 都是自定义组件,在 index.wxml 中引入它们并使其形成嵌套关系 -->
<custom-ul>
<!-- custom-li 通过 slot 渲染到 custom-ul 中 -->
<custom-li></custom-li>
</custom-ul>
<!-- custon-ul.wxml -->
<view>
<view class="header">我是自定义的 ul</view>
<slot></slot>
</view>
// custom-ul.js
Component({
relations: {
'../custom-li/custom-li': {
type: 'child', // 关联的目标节点应为子节点
linked: function(target) {
// 每次有 custom-li 插入时触发,target 是插入的 custom-li 节点的实例对象
},
}
},
lifetimes: {
ready: function(){
this.getLiNodes()
}
},
methods: {
getLiNodes: function(){
// 使用 getRelationNodes() 可以获得 nodes 数组,包含所有已关联的且有序的 custom-li 实例对象
const nodes = this.getRelationNodes('../custom-li/custom-li')
console.log(nodes[0].data)
}
},
})
// custom-li.js
Component({
relations: {
'../custom-ul/custom-ul': {
type: 'parent', // 关联的目标节点应为父节点
linked: function(target) {
// 每次被插入到 custom-ul 时执行,target 是被插入的 custom-ul 节点的实例对象
},
}
},
lifetimes: {
ready: function(){
this.getUlNodes()
}
},
methods: {
getUlNodes: function(){
// 使用 getRelationNodes() 可以获得 nodes 数组,包含所有已关联的且有序的 custom-ul 实例对象
const nodes = this.getRelationNodes('../custom-ul/custom-ul')
nodes[0].setData({type: 'ul'})
}
},
})
自定义组件扩展:
自定义组件扩展其实就是提供了修改自定义组件定义段的能力。Behavior() 构造器提供了新的定义段 definitionFilter ,用于支持自定义组件扩展。 definitionFilter 是一个函数,在被调用时会注入两个参数,第一个参数是使用该 behavior 的 component/behavior 的定义对象,第二个参数是该 behavior 所使用的 behavior 的 definitionFilter 函数列表。
// behavior.js
module.exports = Behavior({
definitionFilter(defFields) {
defFields.data.from = 'behavior'
},
})
// component.js
Component({
data: {
from: 'component'
},
behaviors: [require('behavior.js')],
ready() {
console.log(this.data.from) // 此处会发现输出 behavior 而不是 component
}
})
纯数据字段:
纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能。指定纯数据字段的方法是在 Component 构造器的 options 定义段中指定 pureDataPattern 为一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段。
Component({
options: {
pureDataPattern: /^_/,
},
})
有些情况下,某些 properties 和 data 中的字段既不会展示在页面上,也不会传递给其他组件,仅仅在当前组件内部使用。此时,可以指定这样的数据字段为“纯数据字段”,它们将仅仅被记录在 this.data 中,而不参与任何界面渲染过程,这样有助于提升页面更新性能。
可以使用数据监听器监听纯数据字段。
Component({
options: {
pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
},
properties: {
// 普通属性字段
c: {
type: Boolean,
value: false,
},
// 纯数据属性字段
_d: {
type: Boolean,
value: false,
},
}
data: {
a: true, // 普通数据字段
_b: true, // 纯数据字段
},
methods: {
myMethod() {
this.setData({
c: true, // 普通数据字段
_d: true, // 纯数据字段
})
}
}
})
<view wx:if="{{a}}"> 这行会被展示 </view>
<view wx:if="{{_b}}"> 这行不会被展示 </view>
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)