Component 构造器可用于定义组件,调用 Component 构造器时可以指定组件的属性、数据、方法等。
Component({
behaviors: [],
properties: {
myProperty: {
// 属性名
type: String,
value: '',
},
myProperty2: String, // 简化的定义方式
},
data: {}, // 私有数据,可用于模板渲染
lifetimes: {
created: function () {},
attached: function () {},
detached: function () {},
},
pageLifetimes: {
// 组件所在页面的生命周期函数
show: function () {},
hide: function () {},
},
methods: {
onMyButtonTap: function () {
this.setData({
// 更新属性和数据的方法与更新页面数据的方法类似
});
},
// 内部方法建议以下划线开头
_myPrivateMethod: function () {
// 这里将 data.A[0].B 设为 'myPrivateData'
this.setData({
'A[0].B': 'myPrivateData',
});
},
_propertyChange: function (newVal, oldVal) {},
},
});
详细的参数含义和使用,请参考以下Component框架接口介绍:
Component(config: Object)
注册自定义组件,接受一个 Object
参数,用于描述组件的逻辑交互行为。
// 文件 /components/foo/index.js
Component({
options: Object,
properties: Object,
observers: Object,
data: Object,
methods: Object,
behaviors: Array,
lifetimes: Object,
pageLifetimes: Object,
relations: Array,
});
完整的配置项如上所示,在自定义组件内部可通过 this
关键字访问实例对象。
组件 this 实例
属性
属性 | 类型 | 描述 |
---|---|---|
is | String | 组件的文件路径 |
id | String | 节点 id |
dataset | String | 节点 dataset |
data | Object | 组件数据,包括内部数据和属性值 |
properties | Object | 组件数据,包括内部数据和属性值(与 data 一致) |
方法
方法名 | 参数 | 描述 |
---|---|---|
setData | Object newData | 设置 data 并执行视图层渲染 |
triggerEvent | String name , Object detail , Object options | 触发事件,参见 组件间通信与事件 |
createSelectorQuery | 创建一个 SelectorQuery 对象,选择器选取范围为这个组件实例内 | |
createIntersectionObserver | 创建一个 IntersectionObserver 对象,选择器选取范围为这个组件实例内 | |
selectComponent | String selector | 使用选择器选择组件实例节点,返回匹配到的第一个组件实例对象 |
selectAllComponents | String selector | 使用选择器选择组件实例节点,返回匹配到的全部组件实例对象组成的数组 |
selectOwnerComponent | 选取当前组件节点所在的组件实例(即组件的引用者),返回它的组件实例对象 | |
getRelationNodes | String relationKey | 获取这个关系所对应的所有关联节点,参见组件间关系 |
options
可声明自定义组件的渲染行为及数据交互等。
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
pureDataPattern | regexp | 否 | 纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能。 |
styleIsolation | string | 否 | 组件样式隔离配置,可声明当前组件样式的渲染方式。 |
multipleSlots | boolean | 否 | 启动组件定义多 slot 特性。 |
addGlobalClass | boolean | 否 | 添加全局样式,等同于 styleIsolation=apply-shared 优先级低于 styleIsolation 配置 |
styleIsolation 组件样式隔离
从基础库版本 2.0.0 开始支持。它支持以下取值:
isolated
表示启用样式隔离,在自定义组件内外,使用class
指定的样式将不会相互影响(一般情况下的默认值);apply-shared
表示页面css
样式将影响到自定义组件,但自定义组件css
中指定的样式不会影响页面;shared
表示页面css
样式将影响到自定义组件,自定义组件css
中指定的样式也会影响页面和其他设置了apply-shared
或shared
的自定义组件。(这个选项在插件中不可用。)
properties
声明自定义组件的属性配置及数据处理,每一个键值代表一个属性名,使用驼峰写法,通过 type
定义属性值的类型:String
Number
Boolean
Object
Array
其一,也可以为 null
表示不限制类型。value
可声明默认值,不设置则值为 null
,并可通过 observer()
监听属性值的变化。
Component({
properties: {
myName: {
type: String,
value: 'smart',
observer(newValue, oldValue) {
// do something
},
},
},
});
注意:在 properties
定义段中,属性名采用驼峰写法(myName
);在 tyml
中,指定属性值时则对应使用连字符写法(<tag-name my-name="smart" />
)。
tips
多数情况下,属性最好指定一个确切的类型。这样,在 tyml
中以字面量指定属性值时,值可以获得一个确切的类型,如:
<custom-comp min="1" max="5" />
此时,由于自定义组件的对应属性被规定为 Number
类型, min
和 max
会被赋值为 1
和 5
,而非 "1"
和 "5"
,即:
this.data.min === 1; // true
this.data.max === 5; // true
observers
数据监听器可以用于监听和响应任何属性(properties)和数据 (data)字段的变化。通过定义以属性名、数据名、通配符 *
的函数处理响应。
Component({
observers: {
'value1, value2': function (value1, value2) {
// this.setData 对应数据时触发
},
'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];
},
'some.field.**': function (field) {
// 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发
// (除此以外,使用 setData 设置 this.data.some 也会触发)
field === this.data.some.field;
},
'**': function () {
// 每次 setData 都触发
},
},
});
注意事项
- 数据监听器监听的是
setData
涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依然会被触发。 - 如果在数据监听器函数中使用
setData
设置本身监听的数据字段,可能会导致死循环,需要特别留意。 - 数据监听器和属性的
properties.field.observer
相比,数据监听器更强大且通常具有更好的性能。
data
自定义组件的数据对象和 properties
一同用于组件模板的渲染。
Component({
properties: {
name: {
type: String,
value: 'smart',
},
},
data: { age: 18 },
});
<view>{{name}}: {{age}}</view>
methods
自定义组件的逻辑交互行为,结合组件实例方法实现响应用户操作、数据变更、事件通信等,在函数内通过 this
可访问组件实例。
Component({
methods: {
say() {
this.setData({ name: 'hello world' });
},
},
});
lifetimes
自定义组件的生命周期,可用于响应事件处理。
生命周期 | 参数 | 描述 |
---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 |
attached | 无 | 在组件实例进入页面节点树时执行 |
ready | 无 | 在组件在视图层布局完成后执行, 节点的查找应在此阶段执行 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 |
detached | 无 | 在组件实例被从页面节点树移除时执行 |
error | Object Error | 每当组件方法抛出错误时执行 |
Component({
lifetimes: {
attached() {
// Do something when attached
},
ready() {
// Do something when ready
},
},
});
pageLifetimes
自定义组件所在页面的生命周期声明对象,可用于响应事件处理。
生命周期 | 参数 | 描述 | |
---|---|---|---|
show | 无 | 组件所在的页面被展示时执行 | |
hide | 无 | 组件所在的页面被隐藏时执行 | |
resize | 无 | 组件所在的页面尺寸变化时执行 | 2.6.2 |
externalClasses
组件接受的外部样式类,参见 外部样式类。
relations
组件间关系,定义和使用组件间关系。
// path/to/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 生命周期之后
},
},
},
});
// path/to/custom-li.js
Component({
relations: {
'./custom-ul': {
type: 'parent', // 关联的目标节点应为父节点
linked(target) {
// 每次被插入到 custom-ul 时执行,target 是 custom-ul 节点实例对象,触发在attached生命周期之后
},
linkChanged(target) {
// 每次被移动后执行,target 是 custom-ul 节点实例对象,触发在moved生命周期之后
},
unlinked(target) {
// 每次被移除时执行,target 是 custom-ul 节点实例对象,触发在detached生命周期之后
},
},
},
});
// 注意:必须在两个组件定义中都加入relations定义,否则不会生效。
relations 定义段
relations 定义段包含目标组件路径及其对应选项,可包含的选项见下表。
选项 | 类型 | 是否必填 | 描述 |
---|---|---|---|
type | String | 是 | 目标组件的相对关系,可选的值为 parent 、 child 、 ancestor 、 descendant |
linked | Function | 否 | 关系生命周期函数,当关系被建立在页面节点树中时触发,触发时机在组件 attached 生命周期之后 |
linkChanged | Function | 否 | 关系生命周期函数,当关系在页面节点树中发生改变时触发,触发时机在组件 moved 生命周期之后 |
unlinked | Function | 否 | 关系生命周期函数,当关系脱离页面节点树时触发,触发时机在组件 detached 生命周期之后 |
target | String | 否 | 如果这一项被设置,则它表示关联的目标节点所应具有的 behavior ,所有拥有这一 behavior 的组件节点都会被关联 |
behaviors
用于自定义组件间代码共享的特性,类似于一些编程语言中的 mixins
或 traits
。每个 behavior
可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior
,behavior
也可以引用其它 behavior
。
在组件中使用
组件引用时,在 behaviors
定义段中将它们逐个列出即可。
// my-behavior.js
export default Behavior({
behaviors: [
/* 可引用其他 behavior */
],
properties: { myBehaviorProperty: { type: String } },
data: { myBehaviorData: {} },
lifetimes: { attached() {} },
methods: { myBehaviorMethod() {} },
});
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 被一个组件多次引用,它定义的生命周期函数只会被执行一次。
definitionFilter
定义段过滤器,用于自定义组件扩展。
// behavior.js
export default Behavior({
definitionFilter(defFields) {
defFields.data.from = 'behavior';
},
});
// component.js
import myBehavior from './behavior.js'
Component({
data: {
from: 'component',
},
behaviors: [myBehavior],
ready() {
console.log(this.data.from); // 此处会发现输出 behavior 而不是 component
},
});
通过例子可以发现,自定义组件的扩展其实就是提供了修改自定义组件定义段的能力,上述例子就是修改了自定义组件中的 data 定义段里的内容。
高级进阶
组件间通信与事件
组件间的基本通信方式有以下几种。
- TYML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据。
- 事件:用于子组件向父组件传递数据,可以传递任意数据。
- 如果以上两种方式不足以满足需要,父组件还可以通过
this.selectComponent
方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
监听事件
事件系统是组件间通信的主要方式之一。自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件。
监听自定义组件事件的方法与监听基础组件事件的方法完全一致:
示例代码:
<!-- 当自定义组件触发 myevent 事件时,调用“onMyEvent”方法 -->
<component-tag-name bind:myevent="onMyEvent" />
Page({
onMyEvent(event) {
event.detail; // 自定义组件触发事件时提供的detail对象
},
});
触发事件
自定义组件触发事件时,需要使用 triggerEvent
方法,指定事件名、detail 对象和事件选项:
示例代码:
<!-- 在自定义组件中 -->
<button bind:tap="onTap">点击这个按钮将触发“myevent”事件</button>
Component({
properties: {},
methods: {
onTap() {
var myEventDetail = {}; // detail对象,提供给事件监听函数
this.triggerEvent('myevent', myEventDetail);
},
},
});
组件模板的 slot
在组件的 tyml 中可以包含 slot 节点,用于承载组件使用者提供的 tyml 结构。
一个组件的 tyml 中支持有一个 slot 或者多个 slot(需启用 multipleSlots: true
)
在组件的 tyml 中使用多个 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>
组件数据中的纯数据字段
有些情况下,某些 data
中的字段(包括 setData
设置的字段)既不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用。
此时,可以指定这样的数据字段为“纯数据字段”,它们将仅仅被记录在 this.data
中,而不参与任何界面渲染过程,这样有助于提升页面更新性能。
指定“纯数据字段”的方法是在 Component
构造器的 options
定义段中指定 pureDataPattern
为一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段。
示例代码:
Component({
options: {
pureDataPattern: /^_/, // 指定所有 _ 开头的数据字段为纯数据字段
},
data: {
a: true, // 普通数据字段
_b: true, // 纯数据字段
},
methods: {
myMethod() {
this.data._b; // 纯数据字段可以在 this.data 中获取
this.setData({
c: true, // 普通数据字段
_d: true, // 纯数据字段
});
},
},
});
上述组件中的纯数据字段不会被应用到 TYML 上:
<view ty:if="{{a}}"> 这行会被展示 </view>
<view ty:if="{{_b}}"> 这行不会被展示 </view>
组件属性中的纯数据字段
属性也可以被指定为纯数据字段(遵循 pureDataPattern
的正则表达式)。
属性中的纯数据字段可以像普通属性一样接收外部传入的属性值,但不能将它直接用于组件自身的 TYML 中。
示例代码:
Component({
options: {
pureDataPattern: /^_/,
},
properties: {
a: Boolean,
_b: {
type: Boolean,
observer() {
// 不要这样做!这个 observer 永远不会被触发
},
},
},
});
注意:属性中的纯数据字段的属性 observer
永远不会触发!如果想要监听属性值变化,使用 数据监听器 代替。
使用数据监听器监听纯数据字段
数据监听器可以用于监听纯数据字段(与普通数据字段一样)。这样,可以通过监听、响应纯数据字段的变化来改变界面。
下面的示例是一个将 JavaScript
时间戳转换为可读时间的自定义组件。
Component({
options: {
pureDataPattern: /^timestamp$/, // 将 timestamp 属性指定为纯数据字段
},
properties: {
timestamp: Number,
},
observers: {
timestamp() {
// timestamp 被设置时,将它展示为可读时间字符串
var timeString = new Date(this.data.timestamp).toLocaleString();
this.setData({
timeString: timeString,
});
},
},
});
<view>{{timeString}}</view>