小议Vue对不同类型数据的更新检测

首先我们知道,Vue实现了双向绑定和响应依赖,当数据变化时,Vue能够智能地计算出重新渲染组件的最小代价并应用到DOM操作上。

我们还知道,Vue数组的更新检测和对对象的更改检测是有限制的。那么对SetMap等常用的数据类型,Vue的监测是否有效呢?如果无效,有没有弥补的方法?

下文的讲述将基于这个网页。结构很简单。

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
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-table-render</title>
</head>
<body>
<div id="app">
<p>数组的列表渲染</p>
<ul>
<li v-for="item in arr">{{ item }}</li>
</ul>
<p>数组的条件渲染</p>
<div v-if="arr.length > 3">数组长度大于3</div>
<div v-else>数组长度不大于3</div>
<p>相关依赖是数组的计算属性渲染</p>
<ul>
<li v-for="item in doubleArr">{{ item }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [0, 1, 2],
set: new Set([0])
},
computed: {
doubleArr() {
return this.arr.map(item => item*2);
},
}
})
</script>
</body>
</html>

在浏览器里打开这个网页,打开控制台,输入app.arr.push(3)。嗯,一切正常。接下来试试集合。

因为v-for指令不能直接用于集合,集合的列表渲染可以这样写——

<p>集合的列表渲染</p>
<ul>
//使用扩展运算符,将集合转成数组。
//用Array.from(set)也可以。
    <li v-for="item in [...set]">{{ item }}</li>
</ul>

顺便也试试集合的条件渲染——

<p>集合的条件渲染</p>
<div v-if="set.has(1)">现在集合里有1了~</div>
<div v-else>集合里没有1</div>

还有集合的计算属性渲染——

<p>相关依赖是集合的计算属性渲染</p>
<ul>
    <li v-for="item in [...doubleSetComputed]">{{ item }}</li>
</ul>
......
computed: {
    ......
    doubleSetComputed() {
        return new Set([...this.set].map(item => item*2));
    }
}

修改网页,再打开控制台,输入app.set.add(1)。不出所料,没动静。所幸Vue提供了一个叫做forceUpdate()的方法。

再输入app.$forceUpdate()。集合的列表渲染和条件渲染都正常了,但是计算属性纹丝不动,为啥呢?Vue的文档做得是真心好,也提供了相关答案:计算属性的缓存-vs-method-方法。简单来说,集合不是Vue所认为的响应式依赖,计算属性在这里失灵了。

解决方法也不难,这里不用计算属性,改用method方法——

<p>相关依赖是集合的method方法渲染</p>
<ul>
    <li v-for="item in [...doubleSetMethods()]">{{ item }}</li>
</ul>
methods: {
    doubleSetMethods() {
        return new Set([...this.set].map(item => item*2));
    }
}

再次使用app.$forceUpdate()。成功!

文章到这里接近结束了。篇末做一个总结——

  • Set等数据结构,Vue无法做到响应式依赖。
  • 可以使用forceUpdate()强制渲染。
  • 如果相关依赖是Set,请使用method方法而不是计算属性。

再贴上和数据更新检测相关的几篇文章供大家参考——

Vue爬坑日记,数组数据的更新检测
vue.js中$set与数组更新
Vue.set( target, key, value )