小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
1. 侦听器的其它写法
前面我们编写侦听器时,键是要侦听的响应式属性,值是一个函数或者一个对象。其实,值除了可以是函数或对象,还可以是下面两种类型:
- (方法名)字符串;
- (回调)数组;
1.1 字符串方法名
用法如下:
// 字符串方法名
b: 'someMethod',
拿上篇文章中的例子举例,可以这样写(<body> 元素中的代码):
<div id="app"></div>
<template id="my-app">
<h2>{{ info.name }}</h2>
<button @click="changeInfo">修改 info</button>
<button @click="changeInfoName">修改 info.name</button>
</template>
<script src="./js/vue.js"></script>
<script>
const App = {
data() {
return {
info: { name: 'zhj', age: 20 }
}
},
watch: {
// 字符串方法名
info: 'infoChangeHandler'
},
methods: {
changeInfo() {
this.info = { name: 'Filan' };
},
changeInfoName() {
this.info.name = 'Filan';
},
infoChangeHandler(newInfo, oldInfo) {
console.log('newInfo:', newInfo, 'oldInfo', oldInfo);
}
},
template: '#my-app'
};
Vue.createApp(App).mount('#app');
</script>
页面效果和上篇文章中的第一段代码的页面效果是一样的,即默认情况下侦听器只能侦听到数据本身的变化,而不能侦听到数据内部的变化。所以侦听器字符串方法名的写法:
watch: {
// 字符串方法名
info: 'infoChangeHandler'
},
methods: {
infoChangeHandler(newInfo, oldInfo) {
console.log('newInfo:', newInfo, 'oldInfo', oldInfo);
}
}
等价于:
watch: {
info(newInfo, oldInfo) {
console.log('newInfo:', newInfo, 'oldInfo', oldInfo);
}
}
字符串方法名这种写法一般用的较少。
1.2 回调数组
用法如下:
// 你可以传入回调数组,它们会被逐一调用
f: [
'handle1',
function handle2(val, oldVal) {
console.log('handle2 triggered')
},
{
handler: function handle3(val, oldVal) {
console.log('handle3 triggered')
}
/* ... */
}
]
举例如下(<body> 元素中的代码):
<div id="app"></div>
<template id="my-app">
<h2>{{ info.name }}</h2>
<button @click="changeInfo">修改 info</button>
</template>
<script src="./js/vue.js"></script>
<script>
const App = {
data() {
return {
info: { name: 'zhj', age: 20 }
}
},
watch: {
info: [
'handle1',
function handle2(val, oldVal) {
console.log('handle2 triggered');
},
{
handler: function handle3(val, oldVal) {
console.log('handle3 triggered');
},
/* ... */
}
]
},
methods: {
changeInfo() {
this.info = { name: 'Filan' };
},
handle1() {
console.log('handle1 triggered');
}
},
template: '#my-app'
};
Vue.createApp(App).mount('#app');
</script>
页面效果如下:
1.3 侦听对象的某个属性
这种写法在 Vue3 文档中没有提到,但在 Vue2 文档中有提到:
watch: {
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
前面我们讲到侦听器的配置选项 deep 设置为 true 后,就可以侦听到对象中所有属性的变化了,而如果我们只想侦听对象中某个属性的变化,比如 4.2 节的例子中 info 对象中 name 属性的变化,就可以这样写:
watch: {
// 注意写成字符串格式
'info.name': function(newName, oldName) {
console.log(newName, oldName);
}
}
对于 'info.name',Vue 内部会帮我们解析出要侦听的是 info 对象的 name 属性。
不过,直接侦听数组中对象元素的某个属性是侦听不到的:
data() {
return {
friends: [{ name: 'zhj' }, { name: 'Filan' }]
}
},
watch: {
// 无效侦听,直接侦听数组元素对象的某个属性是侦听不到的
'friends[0].name': function(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
如果真想侦听数组中对象元素的某个属性,有两种办法:
-
侦听数组本身,同时设置
deep选项为true:watch: { 'friends': { handler(newVal, oldVal) { // ... }, deep: true } } -
(推荐)开发中通常情况下我们会对数组进行遍历展示,而数组的每个元素都可以封装成一个组件,该组件中可以通过在
props选项中定义一个属性拿到数组的元素对象,所以我们就可以直接在该组件中侦听此对象的某个属性。实现流程如下:
1.4 实例方法 $watch
我们还可以在生命周期钩子 created() 函数(当组件创建完成后,会自动执行这个函数中的代码,这个函数和 data、computed、watch 等选项是处于同级的,具体后续会讲到)中,通过 this.$watch() 这个 API 来进行侦听:
- 第一个参数是要侦听的源;
- 第二个参数是侦听的回调函数;
- 第三个参数是可选的,是一个对象,里面有三个属性可以设置:
deep、immediate、flush;
$watch 会返回一个函数,后续如果我们想取消侦听,就可以调用这个函数(而写在 watch 选项中的侦听器则不能取消)。
示例:
data() {
return {
info: { name: 'zhj', age: 20 },
message: '你好'
}
},
created() {
// console.log(this.message); // 你好
// 这里的回调函数可以写成箭头函数,因为这里箭头函数中的 this 就是外层作用域 created 函数中的 this
const unwatch = this.$watch('info', (newInfo, oldInfo) => {
console.log(newInfo, oldInfo);
// console.log(this.message); // 你好
}, {
deep: true,
immediate: true
});
// 取消侦听
// unwatch();
}
当然,在开发中,我们通常使用的侦听器是比较简单的,很少遇到非常复杂的情况。