组件对象
组件对象
位于 UX 文件内的 <script>
标签定义并导出了一个组件对象。一个典型的组件对象定义如下:
export default {
data: {
text: "Hello world"
},
onInit() {
console.log("component onInit()")
},
clicked(event) {
console.log(`clicked: ${event}`)
}
}
组件框架允许开发者为组件对象填写一些属性来实现功能,本文档将介绍这些属性。
响应式编程
响应式编程是一种用于动态更新界面和数据状态的编程范式。通过响应式属性,开发者可以自动追踪数据的变化并更新界面,无需手动触发和管理这些更新。这使得数据与界面始终保持同步,实现简洁高效的 UI 编程体验。
响应式属性
组件对象的 data
属性和 computed
属性对象中定义的属性都是组件的响应式属性,也称为 view-model 属性:
data
属性:直接反映组件的状态。例如,温度值、显示文本或按钮状态等都可以定义在data
中。当这些属性值发生变化时,框架会自动同步到视图中。computed
属性:用于定义基于data
或其他computed
属性计算得到的派生属性。计算属性会自动随依赖数据的变化而更新,使得复杂的逻辑表达更直观、简洁。
总而言之,当组件的响应式属性值发生变化时,依赖这些属性的内容会自动更新并进行渲染,从而保证显示的内容与数据保持一致。
自动数据绑定
自动数据绑定是响应式编程的核心概念,它使得数据的变化能够直接反映到界面上,而无需开发者手动处理。
由于每个响应式属性与界面的相关部分是自动绑定的,当属性值发生变化时,界面会自动更新,无需调用特定元素的属性更新函数。
例如定义一个名为 counter
的响应式属性:
export default {
data: { // 将 counter 响应式属性定义在 data 对象中
counter: 0 // 初始值为 0
}
}
每当 counter
的值发生变化,引用该属性的界面也会自动更新。下面的模板代码演示了这个机制:
<p on:click="counter += 1">
counter: {{ counter }}
</p>
此示例演示了点击 <p>
标签时会使 counter
显示值加 1 的计数器。你可以点击下面的在线 demo 来测试它:
<p>
标签内的 {{ counter }}
是一个模板插值表达式,它对 counter
的依赖是自动绑定的。而 <p>
标签中的 on:click
监听在点击时修改 counter
属性值。可以看到,通过自动数据绑定的方式,消除了传统 GUI 开发中的手动数据-界面更新的操作,使界面逻辑更加简洁明了。
data
属性
必要性:可选
类型:Object
data
属性是一个对象,例如:
export default {
data: {
text: "Hello world"
}
}
data
属性的字面量要能通过 json.stringify()
进行序列化,准确来说必须满足下列条件:
- 简单类型的值:number、string、boolean、
null
或undefined
- 具有递归结构的 object 和 array 中,最深层元素的值必须属于上述中的一种
这意味着源代码中 data
对象的属性不能有函数或其他特殊类型的值,但是可以在运行时将任何值赋值给 data
属性。
data
属性都是组件的 view-model 属性,因此其中数据可用于响应式编程。在组件对象中使用 this.prop
的写法可以直接访问 data
对象中的属性。因此,在下面的组件对象中
export default {
data: {
onInit: true
},
onInit() {}
}
代码 this.onInit
将会访问 data
对象中的 onInit
属性,而不是生命周期函数 onInit
。
computed
属性
必要性:可选
类型:Object
组件对象的 computed
属性对象声明组件中的计算属性。相比于 data
中的响应式属性,计算属性可以实现需要一些计算才能得到结果的属性。例如
<text> reversed message: {{ reversedMessage }}
export default {
data: {
message: "hello"
},
computed: {
reversedMessage() { // 这是 reversedMessage 计算属性的 getter 方法
return this.message.split('').reverse().join('')
}
}
}
这里声明了一个 reversedMessage
计算属性,该属性实现了一个 getter 函数用于获取属性值。直接使用 this.reversedMessage
(在模板中可以省略 this.
)即可获取该计算属性的值。
计算属性也是组件的 view-model 属性。计算属性的值会被缓存,因此多次获取计算属性的值也不会重复计算。另一方面,计算属性会所依赖的 view-model 属性变化后会自动更新。在这个例子中,计算属性的值是由 message
属性计算得出的,因此 message
属性变化时,reversedMessage
属性的值会自动更新。
计算属性的 setter 方法
默认的计算属性只有 getter 方法,但你还可以为计算属性提供 setter 方法:
export default {
data: {
message: "hello"
},
computed: {
reversedMessage: {
get() { // 这是 reversedMessage 计算属性的 getter 方法
return this.message.split('').reverse().join('')
},
set(value) {
this.message = value.split('').reverse().join('')
}
}
}
}
此时,计算属性 reversedMessage
的值不再是一个函数,而是一个对象,后者有两个方法:getter 方法 get
和 setter 方法 set
。set
方法的参数就是计算属性需要被设置的新值。
watch
属性
watch
对象方法用于监听 view-model 属性的变化,例如:
export default {
data: {
value: 0
},
watch: {
value(newValue, oldValue) {
console.log(`value change: ${oldValue} -> ${newValue}`)
}
}
}
watch
对象的方法会监听同名 view-model 属性的变化,因此 watch.value()
监听 value
属性变化。计算属性的变化也可以由 watch
监听。
生命周期函数
详见生命周期文档。
自定义属性
用户还可以在组件对象中定义自定义属性,这些属性不在 view-model 中,因此不是是响应式的。开发者可以将方法定义为自定义属性,还可以使用自定义属性存储一些不需要响应式的数据。例如:
<p on:click="onClick()">{{ text }}</p>
export default {
data: {
text: "some text"
},
onInit() {
// 对 this 赋值的新属性是自定义属性
this.timer = setInterval(() => this.text += "?", 1000)
},
onDestroy() {
clearInterval(this.timer)
},
onClick() {
this.text += "." // 在自定义方法中操作 view-model 属性
}
}
例子中的 text
属性是响应式的,而 timer
属性则是非响应式的自定义属性。例子中的 timer
属性用于存储定时器句柄值,这个值和界面视图没有关系因此不需要作为 view-model 属性。考虑到代码的规范性,也可以在组件对象中事先定义自定义属性:
export default {
data: {
text: "some text"
},
timer: null, // 自定义属性是组件对象的直接属性
// ...
}
如例子中所示,自定义属性直接定义在组件对象内即可。每个组件的自定义属性都是不同的实例而不会共享。
注意
自定义属性、data
对象、 computed
对象、生命周期函数等属性都不能出现重名,否则会使某些属性被覆盖而无法访问。