浅析vue2中computed和watch
2024-12-09 11:13:16

这依旧是一个经典的,几乎被问烂的面试八股文,只要是vue技术栈的前端开发者,在入门的时候都会刷到相关面试题。

但是看到知乎上总有人挑刺,于是这里就花点时间整理一下,省的有人说我回答的不全面。

正文

虽然这次的问题比较简单,但是按照惯例,还是从写法上稍微看看。

computed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<body>
<div id="app">
姓: <input type="text" v-model=firstName> 名:
<input type="text" v-model=lastName> 姓名:
<span>{{fullname}}</span>
<span>{{fullnameTest}}</span>
</div>
</body>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
firstName: 'z',
lastName: 's'
},
computed: {
// 常规写法
fullname() {
return this.firstName + this.lastName
}
// 完整写法
fullnameTest : {
set(val){
console.log('这里获取到了数据',val)
}
get(){
console.log('这里处理好了了数据',this.firstName + this.lastName)
return this.firstName + this.lastName
}
}
}
})
</script>

命名唯一性

computed是data中的变量的一个补充,很多教程和面试题中,都没有提到一点,那就是computed中声明的变量,在data中是不能用同名变量的。

因为不管是 computed 属性名还是 data 数据名还是 props 数据名 都会被挂载在 vm 实例上,因此这三个都不能同名。

支持数据缓存

这里很多面试八股的时候,都会说提一嘴支持数据缓存,那什么是数据缓存呢?这里今天会着重讲一下,这也是computed的核心用法。

比如,我们在页面中用到了一次computed的数据渲染,那这个时候,computed渲染数据的方法就调用了一次,那如果页面用到两次呢?

对的,就是这样的,页面中如果用到了两次,依然只是执行了一次,因为数据本身是被缓存起来的,只要没有什么数据更新的话,这里的computed属性依然只进行了一次计算,多次渲染。

这里是需要和methods属性做比较的,很多开发者可能不知道,methods中声明的方法,实际上也可以返回一个数据,然后在页面渲染中使用,只是很少有人这么用罢了。

那么,methods中的方法,在页面渲染中多次使用,methods会被调用多少次呢?

答案很显而易见,会被多次调用,因为methods并不支持缓存。

1
注:render响应最小更新的颗粒度是组件而非指令,在vue1升级到vue2的时候有此类区别

支持缓存的最大好处,就在于可以减少页面性能的开销,如果一个数据需要多次在页面中渲染,而且又涉及到颇多计算的时候,用computed渲染显然是一个好选择。

1
2
computed计算出一个新属性,每个新属性对应一个computedwatcher,依赖数据更新时会把watcher中的dirty的值改成true,只有在render过程中读取新属性时进行判断,dirty为false返回原结果,dirty为true才进行计算,这就是数据缓存的基本原理,详情这里就不展开细说了。
注意,computed计算缓存类型支持所有数据类型,并非只支持基本数据类型,数据对象这类引用类的数据类型也是支持的。

以上是我早年对computed的理解,应该算是相对完善的回答,但是问的更深,我就答不出来了。

至于实现原理 ,我这里就不展开详细说明,因为太过久远,我梳理起来也费劲,个人推荐B站的一个视频:vue的computed为什么可以缓存数据?原理是什么,对源码的解析相对较为不错,只不过似乎视频是别人公众号的,重复发的太多了,我就随便挑了一个。

不支持异步

业务场景中常见的一个问题,有时候有人会想在computed中放一些异步函数获取数据,实际上则无法获取。

watch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<div id="app">
姓: <input type="text" v-model=firstName> 名:
<input type="text" v-model=lastName> 姓名:
<span>{{fullname}}</span>
</div>
</body>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
firstName: 'z',
lastName: 's',
fullname: 'zs'
},
watch: {
firstName(newval) {
this.fullname = newval + this.lastName
},
lastName(newval) {
this.fullname = this.firstName + newval
}
}
})
</script>

这里就不说什么watch支持不支持缓存了,数据缓存+页面渲染,那时methods对标computed事情,和watch是没有关系的。

面试题时候提一嘴,算是给面子了,实际应用中,watch本来就和缓存不沾边,毕竟只是用来做数据监听的,肯定是数据一变化,watch这边就要响应。

支持异步

这是watch最常用的场景,一旦数据发生变化,立刻会有选择项的获取列表发生变化,这在实际开发中常用来做表单填写的监听判断。

二者区别

功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

是否调用缓存

computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取

watch在每次监听的值发生变化的时候都会执行回调

是否调用return

computed中的函数必须要用return返回,

watch中的函数不是必须要用return。

是否首次监听

computed默认第一次加载的时候就开始监听;

watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true

应用场景

computed:当一个属性受多个属性影响的时候,使用computed,经典的购物车结算类的操作。

watch:当一条数据影响多条数据的时候,且有异步计算相关的时候,使用watch,例如搜索框,或者表单中,一条数据会干扰多条数据的使用。

结语

这里只是浅层的从应用层面说了一下,因为时间关系,自己这里就不涉及到源码进行详细解析了。

computed与watch,早年还有面试官提问哪个更优更劣的问题,这种问题就是纯粹的陷阱问题,又或者面试官本人水平很菜。

二者并无更优更劣的说法,只有谁更适合什么场景,脱离应用场景去说优劣就是没有理解开发的本质。

参考

API — Vue.js (vuejs.org) - computed

API — Vue.js (vuejs.org) - watch

computed缓存指的是什么意思?随便举几个例子就行

watch跟computed的区别