这个知识点经常问,本来我是想把这个放在一个大类里面说的,但是发现越写越多,就干脆单独拿出来用一篇来写了,在这里我就总结一下我个人对这两个知识点的理解。其中参考了这位大佬的文章《vue watch原理》,不胜感激。
侦听器watch(一对多)
-
作用
简而言之就是监听数据的变化,可以通过监听某个数据的变化来进行其他操作。也就是 一对多(一个数据影响多个数据) 的关系。
-
常用属性
- immediate:在选项参数中指定
immediate: true
会在初始化watch时就立即执行handler回调函数,而不是等下一次数据更新。注意在带有immediate
选项时,你不能在第一次回调时取消侦听给定的property
。
export default { data() { return { foo: "123", }; }, watch: { foo: { handler(newData, oldData) { console.log("newData:", newData); //123 console.log("oldData:", oldData); //undefined }, immediate: true,//这个属性设置为true以后就会立马触发监听事件显示打印,如果是false,则此时不会有打印数据,要等到修改foo才会触发监听事件。 }, }, };
- deep:为了发现内部值的变化可以设置
deep: true
,监听数组的变更则不需要这么做,因为数组用this.$set方法是可以直接检测到的。但是如果把数组对象的某个元素的其中一个属性直接替换,还是要加deep: true
。
export default { data() { return { foo: { name: "yyy", age: "12", }, }; }, watch: { foo: { handler(newData, oldData) { console.log("newData:", newData.age); console.log("oldData:", oldData.age); }, deep: true,//如果deep值不为true,不会触发handler事件 }, }, mounted() { this.foo.age = "321"; }, };
- immediate:在选项参数中指定
-
用法
-
监听基础数据类型
export default { data() { return { foo: "123", }; }, watch: { foo(newData, oldData) { console.log("newData:", newData); //321 console.log("oldData:", oldData); //123 }, }, mounted() { this.foo = "321"; }, };
-
监听对象
- 监听整个对象
- 替换和变更的结果是不一样的,如上图所言,变更的前后值是相同的,替换则能拿到旧值。
export default { data() { return { foo: { name: "xxx", id: "123", class: "一", }, bar: { name: "xxx", id: "123", class: "一", }, }; }, watch: { foo: { handler(newData, oldData) { console.log("newData:", newData.name); //yyy console.log("oldData:", oldData.name); //yyy }, deep: true, }, bar: { handler(newData, oldData) { console.log("newData:", newData.name); //yyy console.log("oldData:", oldData.name); //xxx }, deep: true, }, }, mounted() { this.foo.name = "yyy"; this.bar = { name: "yyy", id: "321", class: "一二", }; }, };
- 监听单个属性
export default { data() { return { foo: { name: "xxx", id: "123", class: "一", }, }; }, watch: { 'foo.name'(newData, oldData) { console.log("newData:", newData); //yyy console.log("oldData:", oldData); //xxx }, }, mounted() { //只触发一次监听 this.foo.name = "yyy"; setTimeout(() => { this.foo.id = "321"; }, 1000); }, };
- 监听整个对象
-
监听数组
- 由于新旧数据是相同的,可以借用计算属性深拷贝可以避免这种情况。监听对象也是同理。
export default { data() { return { foo: [1, 2, 3, 4, 5], }; }, computed: { bar() { return JSON.parse(JSON.stringify(this.foo)); }, }, watch: { foo(newData, oldData) { console.log("newData:", newData); //[1, 22, 3, 4, 5] console.log("oldData:", oldData); //[1, 22, 3, 4, 5] }, bar(newData, oldData) { console.log("newData:", newData); //[1, 22, 3, 4, 5] console.log("oldData:", oldData); //[1, 2, 3, 4, 5] }, }, mounted() { this.$set(this.foo, 1, 22); }, };
-
-
特点
- watch允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。计算属性会立即返回,而此时异步操作可能还没有完成;
- 如果前后两个值相同,不会触发侦听器;
export default { data() { return { foo: "123", }; }, watch: { foo(val) { setTimeout(() => { console.log("foo-data", val); //可以触发 }, 1000); }, bar(val) { console.log("bar-data", val); //无法触发 }, }, computed: { bar() { setTimeout(() => { return this.foo; //计算属性会立刻返回值并不能做其他过多操作,如果不用setTimeout方法是可以触发上面bar的watch监听的 }, 1000); }, }, mounted() { this.foo = "321"; }, };
计算属性computer(多对一)
-
作用
通俗来说就是一个值或多个值拼接或者组合而成的一个值,参考变形金刚里的合体金刚大力神(手动狗头)。而且computer中的数据不需要在data中再次声明。
-
用法
export default {
data() {
return {
foo: "123",
bar: "321"
};
},
computed: {
zoo() {
return this.foo + this.bar; //zoo不需要在data中定义,直接用this.zoo调用就可以
},
}
};
-
特点
计算属性是基于它们的响应式依赖来进行缓存的,只有在相关的依赖发生变化的时候才会再次求值计算。这样可以节省想当多的性能,不管是用侦听器还是用函数来达到同样的效果,性能都不如计算属性。