部分自定义组件特性仅适用于 glass-easel 组件框架 。
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
部分自定义组件特性仅适用于 glass-easel 组件框架 。
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
如果 data 中的某个字段是函数,在模板里可以直接调用它:
Component({
data: {
getDataField() {
return 'someValue'
},
},
})
<view>{{ getDataField() }}</view>
尽管这样做有时会很方便,但在实践中依然不建议滥用。
从代码可维护性的角度看, data 中的内容应当与数据内容强相关。如果函数的主要目的是对数据展示方面的预处理,推荐用 WXS 的方式,将函数实现内联在模板中。
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
Chaining API 是一种新的页面和自定义组件定义形式。
对于一个传统的自定义组件定义:
Component({
properties: {
myProperty: String,
myAnotherProperty: String,
},
data: {
myDataField: 'someValue',
},
})
它可以被等价地写成以下 Chaining API 形式:
Component()
.property('myProperty', String)
.property('myAnotherProperty', String)
.data(() => ({
myDataField: 'someValue',
}))
.register()
使用 Chaining API 的主要好处是它具有更好的 TypeScript 支持,且对于复杂组件更加友好,还可以配合 init 函数 来使用。但它也使得对简单组件的定义看起来稍显繁琐。
因而,每个组件都可以分别选用传统的定义方式或者 Chaining API 来进行定义,可以对于每个组件都选用更合适它的定义方式。
以下是一些常用链式调用项。
.property 用来定义单个属性,等价于传统形式的 properties 定义段中的单个项目。例如:
Component()
.property('myProperty', {
type: String
})
.register()
.data 用来定义数据字段表,作用上相当于传统形式的 data 定义段,但它接受一个函数。这个函数在每次组件创建时执行一次,它的返回值被用作数据字段。例如:
Component()
.data(() => ({
myDataField: 'someValue',
}))
.register()
.externalClasses 用来定义外部样式类,等价于传统形式的 externalClasses 定义段。例如:
Component()
.externalClasses(['my-class'])
.register()
.options 用来指定组件选项,等价于传统形式的 options 定义段。(注意,如果多次调用,仅有最后一次调用有效。)例如:
Component()
.options({
multipleSlots: true,
})
.register()
.options 用来指定组件选项,等价于传统形式的 options 定义段。(注意,如果多次调用,仅有最后一次调用有效。)例如:
Component()
.options({
multipleSlots: true,
})
.register()
以下链式调用项也是可用的,但通过 init 函数 来调用通常更加友好。
.methods 用来定义一组方法,等价于传统形式的 methods 定义段。例如:
Component()
.methods({
myMethod() { /* ... */ }
})
.register()
.lifetime 和 .pageLifetime 分别用来定义单个生命周期方法和组件所在页面的生命周期方法,等价于传统形式的 lifetime 和 pageLifetime 定义段中的单个项目。例如:
Component()
.lifetime('attached', function () { /* ... */ })
.pageLifetime('show', function () { /* ... */ })
.register()
.observer 用来定义单个数据监听器,类似于传统形式的 observers 定义段中的单个项目,但在同时监听多个数据字段时,应写成数组形式。例如:
Component()
.data(() => ({
a: 1,
b: 2,
}))
.observer(['a', 'b'], function () { /* ... */ })
.register()
.relation 用来定义单个组件间关系项,等价于传统形式的 relations 定义段中的单个项目。例如:
Component()
.relation('another-component', {
type: 'parent',
})
.register()
类似地, Behavior 也支持 Chaining API 。例如:
const beh = Behavior()
.property('myProperty', String)
.register()
这样,在组件中,可以使用 .behavior 将其引入:
Component()
.behavior(beh)
.register()
需要注意的是,引入 behavior 导致出现了重复的同名属性或同名数据字段时, TypeScript 将会报出类型错误。
除了 options 和 export ,其他链式调用项都可以重复调用多次,调用结果会组合起来。
这样可以把复杂的组件拆解成好几个部分来定义,对于很复杂的组件定义会有帮助。
Component()
// 定义 myDataField 字段和相关的处理逻辑
.data(() => ({
myDataField: 'someValue',
}))
.lifetime('attached', function () {
this.setData({ myDataField: updatedValue })
})
// 定义 anotherField 字段和相关的处理逻辑
.data(() => ({
anotherField: 1,
}))
.lifetime('attached', function () {
this.setData({ anotherField: updatedValue })
})
.register()
链式调用项也可以分开写。例如:
const componentDefinition = Component()
componentDefinition.property('myProperty', String)
componentDefinition.data(() => ({
myDataField: 'someValue',
}))
componentDefinition.register()
但这样写会丢失部分 TypeScript 类型信息。这种做法比较适合制作中间件、将 Component() 封装成别的形式的调用时。手工编写代码时并不建议这么做。
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
在 Chaining API 中支持 .init(...) 链式调用项,可以以另一种方式进行组件创建:
Component()
.data(() => ({
myDataField: 'someValue',
}))
.init(function ({ lifetime }) {
// 这里可以用 JavaScript 局部量
const getUpdatedValue = () => {
return 'updated'
}
// 定义一个生命周期方法
lifetime('attached', () => {
this.setData({ myDataField: getUpdatedValue() })
})
})
.register()
init 中定义的函数会在每次组件创建时被调用一次。
这种方式的主要好处是在其内部可以自由使用 JavaScript 局部变量,减少对组件 this 的使用,有时会很方便。
init 的第一个参数包含多个辅助方法,可以用于组件定义。
method 用来定义单个方法,等价于传统形式的 methods 定义段中的单个项目。不过,它通常只用来定义事件响应函数,而且在末尾需要返回出来。例如:
Component()
.init(function ({ method }) {
const tapHandler = method(() => {
/* ... */
})
return { tapHandler }
})
.register()
lifetime 和 pageLifetime 分别用来定义单个生命周期方法和组件所在页面的生命周期方法,等价于传统形式的 lifetime 和 pageLifetime 定义段中的单个项目。例如:
Component()
.init(function ({ lifetime, pageLifetime }) {
lifetime('attached', () => { /* ... */ })
pageLifetime('show', () => { /* ... */ })
})
.register()
observer 用来定义单个数据监听器,类似于传统形式的 observers 定义段中的单个项目,但在同时监听多个数据字段时,应写成数组形式。例如:
Component()
.data(() => ({
a: 1,
b: 2,
}))
.init(function ({ observer }) {
observer(['a', 'b'], () => { /* ... */ })
})
.register()
relation 用来定义单个组件间关系项,等价于传统形式的 relations 定义段中的单个项目。例如:
Component()
.init(function ({ relation }) {
relation('another-component', {
type: 'parent',
})
})
.register()
需要注意的是,上面这些方法都不能异步或延迟执行,否则会报错:
Component()
.init(function ({ lifetime }) {
setTimeout(() => {
// 不能这么做!
lifetime('attached', () => { /* ... */ })
}, 0)
})
.register()
此外,第一个参数中还包含有 data 和 setData ,可以用来快速访问和设置数据。例如:
Component()
.data(() => ({
myDataField: 'someValue',
}))
.init(function ({ lifetime, data, setData }) {
lifetime('attached', () => {
setData({
myDataField: data.myDataField + ' updated',
})
})
})
.register()
但要注意 data 和 setData 只应在各个回调函数中使用,下面这样做会报错:
Component()
.init(function ({ setData }) {
setData({ /* ... */ })
})
.register()
由于目前 glass-easel 组件框架仅可用于 Skyline 渲染引擎,因此这些特性也同样受此限制。
简单的自定义组件 slot 类型有两种:单一 slot 和多 slot ,取决于自定义组件的 multipleSlots 选项。它们都属于静态 slot 。
它们都要求(相同 name 的) slot 节点只有一个,重复的 <slot /> 中只有第一个会生效。
之所以称其为“静态”,是因为无论组件的实现如何, slot 的内容(由组件使用者提供)只会出现一次,不会因 <slot /> 的重复而重复。这样组件的使用者更容易控制它自身的节点。
从性能上看,单一 slot 也具有相对最优的性能表现。
但有时需要在列表中使用 slot 使得 slot 的内容被重复多次。此时可以使用动态 slot 。
Component({
options: {
dynamicSlots: true, // 启用动态 slot
},
data: {
list: ['A', 'B', 'C'],
},
})
然后,在模板中可以使 <slot /> 重复多次:
<block wx:for="{{ list }}">
<slot />
</block>
在动态 slot 中,被重复的 <slot /> 可以分别携带不同的数据。例如:
<block wx:for="{{ list }}">
<slot list-index="{{ index }}" item="{{ item }}" />
</block>
上述的 slot 中携带有 list-index 和 item 两个数据项。
组件的使用者可以通过 slot: 来接收 slot 传递的任何数据项。例如:
<view>
<child>
<view slot:item>{{ item }}</view>
<view slot:listIndex>{{ listIndex }}</view>
</child>
</view>
组件的使用者在接收 slot 传递的数据项时,可以更改数据项的字段名。例如:
<view>
<child>
<view slot:listIndex="index">{{ index }}</view>
</child>
</view>