| ✍️ Tangxt | ⏳ 2020-07-15 | 🏷️ computed、watch |
computed)我们在模板内可以插入表达式,如🟡🟡message🟡🟡、🟡🟡User.name🟡🟡……
假如我们在模板中插入这样复杂的表达式:
<div id="example">
🟡🟡 message.split('').reverse().join('') 🟡🟡
</div>
可以看到,此时我们并不能一眼就能明白 message.split('').reverse().join('') 在表示什么
也就是说,这个表达式是很「命令式」的,并不像message、User.name这样这么「声明式」!
所以,针对这种有「复杂逻辑」的表达式,我们都应该使用计算属性!反之,如果是「简单运算」,那就不需要用计算属性,直接用 data 里边旗下的属性就好了!
有以下模板:

测试:

计算属性的缓存 vs 方法:

小结:
A 的 getter 了,而这显然有点多余……当然,如果你不希望有缓存,可以用方法代替之……计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:

watch)一种更通用的方式来观察和响应 Vue 实例上的数据变动
简单来说,就是当你有一些数据需要随着其它数据变动而变动时,就可以用该侦听属性 watch
不过该属性过于「命令式」,相较于「计算属性」来说,这很不方便……

可看到,watch里边的代码是很重复的,很命令的,相较于计算属性的版本来说,是比较 low 的:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
还咩有确定一个值到底是多少的时候!
既然尤大大提供了一个 watch 选项,那么肯定会有它使用的场景的:
watch 选项测试 1:
模板:


测试 2:

n+1的时候,n+1是一个简单类型 n 会发生改变。(简单类型的值一定会发生改变,因为简单类型存的是值,复杂类型存的才是地址)
- 当点击obj.a+"hi"的时候里面的值依然会发生改变,因为这是一个对象简单类型。obj=新对象 的时候这个对象的地址会发生改变,vue 才会认为这个对象发生了改变。所以,watch的监听方式是:简单数据类型看值,复杂数据类型(对象)看地址
你可以不用
watch选项 -> 可以使用vm.$watch来自定义侦听器
文档:vm-watch
Tip:watch也可以监听模拟的「计算属性」

我之前一直以为只有写在return旁边的其它属性,如「return xxx」,xxx变化了,计算属性才会跟着变化
没想到,并不需要这样,总之,只要函数体中存在依赖的其它属性就行了!
关于缓存,如果依赖的属性没有变化,就不会重新计算,getter/setter默认不会做缓存,Vue 做了特殊处理。
watch侦听属性用来监听和响应 Vue 实例上的数据变动。watch就是在数据发生变化的时候,进行执行一个函数区别:
computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。
计算结果会被缓存,computed 的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取 computed 的值时才会**重新调用对应的 getter **来计算。
watcher 更像是一个 data 的数据监听回调,当依赖的 data 的数据变化,执行回调,在方法中会传入 newVal 和 oldVal。可以提供输入值无效,提供中间值 特场景。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。如果你需要在某个数据变化时做一些事情,使用watch。
默认值是 false,代表是否深度监听
使用场景:
data是这样的:
{
obj: {
a: 123
}
}
我们watch这个对象:
{
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
}
效果:
deep:true的存在,让obj.a变化了,也会触发handler的执行,不然,默认的deep:false,只有obj的引用地址发生了变化,handler才会去执行!
所以:
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。
因此,我们可以这样优化,使用字符串形式监听:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
这样 vue 才会一层一层解析下去,直到遇到属性a,然后才会给a设置监听函数,而不是obj旗下所有的属性!
因为我们的组件是经常要被销毁的,比如我们跳一个路由,从一个页面跳到另外一个页面,那么原来的页面的 watch 其实就没用了,这时候我们应该注销掉原来页面的 watch 的,不然的话可能会导致内存溢出
当然,我们平时 watch 都是写在组件的选项中的,它会随着组件的销毁而销毁。
然而,如果我们使用下面这样的方式写 watch,那么就要手动注销了,当然,这种注销其实也是很简单:
const unWatch = app.$watch('text', (newVal, oldVal) => {
console.log(`${newVal} : ${oldVal}`);
})
unWatch(); // 手动注销 watch
app.$watch调用后会返回一个值,就是unWatch方法,你要注销 watch 只要调用unWatch方法就可以了。

我们知道观察的数据发生了变化,就会执行callback,可如immediate、deep这样的选项,它们都具备某种特殊的功能,而这些功能在某种场景下会有奇效!
watch属性可以是字符串、函数、对象、数组
{
watch:{
n(){} // 可以是函数
n: 'logN' // 可以是 methods 里面的函数
n: { handler(){} , deep:true }
n: { handler(){} , immediate:true }
n: [f1(){},f2(){}] //都会执行里面的函数
'obj.a'(){} // 监听 obj 里面 a 的值是否变化
}
}
let vm = new Vue({
data: {
user: {
email: "fangfang@qq.com",
nickname: "方方",
phone: "13812312312"
},
displayName: ""
},
watch: {
"user.email": {
handler() {
const { user: { email, nickname, phone } } = this;//从this中解析出user,从user中解析出三个变量
this.displayName = nickname || email || phone;
},
immediate: true // 第一次渲染也触发 watch
},
"user.nickname": {
handler() {
const { user: { email, nickname, phone } } = this;
this.displayName = nickname || email || phone;
},
immediate: true
},
"user.phone": {
handler() {
const { user: { email, nickname, phone } } = this;
this.displayName = nickname || email || phone;
},
immediate: true
}
},
template: `
<div>
🟡🟡displayName🟡🟡
<button @click="user.nickname=undefined">remove nickname</button>
</div>
`,
methods: {
changed() {
console.log(arguments);
const user = this.user;
this.displayName = user.nickname || user.email || user.phone;
}
}
}).$mount("#app");
效果:

➹:浅析Vue进阶属性“computed、watch” - 知乎
➹:認識 Vue.js watch 監聽器. 認識 watch 如何使用、有哪些可選設定
➹:Vue 的 computed 和 watch 的区别 - 知乎
➹:Vue 里的 computed 和 watch 的区别 - 知乎
watch可译为「监听」、「侦听」computed是用来计算一个值的,使用时不需要加括号,可以直接当属性使用。computed拥有依赖缓存特性,如果依赖值不变,computed不会重新计算watch是用来监听的,被监听的属性旗下(取的是{}值),有两个选项,immediate 和 deep,当immediate:true时,表示会在第一次运行(第一次渲染)时执行这个函数,当deep:true时,如果监听一个对象,会同时监听其内部属性。watch 没有依赖缓存特性。 -> 对于vm.$watch来说,语法是这样的:vm.$watch('xxx',handler,{deep: true, immediate: true})watch 了某个data的handler函数,因为箭头函数没有 this,不然,this的指向就是其上一级作用域的this值! -> 大概率是windowcomputed?
computed -> 多个data旗下的数据来搞一个计算属性watch?
watch -> data都被 Vue 内部getter和setter了,我们想要自主的「对数据做一些操作」,只能通过watch来搞了…… -> 乱搞一个data旗下的数据computed和watch有了一些用法认识还不够,你还得写大量的例子去深入理解它们!