背景
最近项目组使用vue代替angularjs,在响应式这一块有很多同事用的不太对,导致数据修改后,页面看起来没有同步“渲染”。下面我写了一个小例子,希望看完以后加深对vue响应式的理解。
对数组元素的修改
1. 直接修改数组元素
首先看如下例子:
<template>
<div id="app">
<h2>测试直接修改简单元素数组的值</h2>
<button @click="setArr1">setArr1</button>
<div>{{arr1}}</div>
<h2>测试直接修改复杂元素数组的值</h2>
<button @click="setArr2">setArr2</button>
<div>{{arr2}}</div>
</div>
</template>
export default {
name: 'App',
data() {
return {
arr1: [1,2,3,4],
arr2: [{'age': 12, name: 'tom'}, {'age': 100, name: 'jerry'}],
}
},
methods: {
setArr1() {
this.arr1[0] = 99
},
setArr2() {
let tom = this.arr2[0]
tom.age = 99
},
}
}
首先我们有2个数组:


下面通过动图来查看修改后的结果:

可以看到:点击setArr1按钮,数组[1,2,3,4]没有被修改成[99,2,3,4]。但是点击setArr2按钮,数组arr2的第一个元素的age属性确实被修改成了99.
此时发现arr1数组的值也发生了变化。这就是由于错误的修改arr1的数据导致的,具体的原因暂不清楚。
2. $set修改数组元素
<template>
<div id="app">
<h2>测试$set修改简单元素数组的值</h2>
<button @click="setArr3">setArr3</button>
<div>{{arr3}}</div>
<h2>测试$set修改复杂元素数组的值</h2>
<button @click="setArr4">setArr4</button>
<div>{{arr4}}</div>
</div>
</template>
export default {
name: 'App',
data() {
return {
arr3: [1,2,3,4],
arr4: [{'age': 12, name: 'tom'}, {'age': 100, name: 'jerry'}],
}
},
methods: {
setArr3() {
this.$set(this.arr3, 0, 99)
},
setArr4() {
let tom = this.arr4[0]
tom.age = 99
this.$set(this.arr4, 0, tom)
},
}
}
这次使用$set来修改数组元素,下面上动图:

这次可以看到,点击按钮后数组元素的修改都成功了。
3. 修改数组元素的总结
其实很简单,就2种情况:
- 数组元素是
number、string等基本类型时,要用$set来修改 - 数组元素是
object、array等复杂类型时,可以直接用"."或者中括号[]来进行赋值操作。当前,用$set也是可以的。
进阶:对对象属性的修改
看如下例子:
<template>
<div id="app">
<h2>测试直接给对象新增属性</h2>
<button @click="setAttribute1">setAttribute1</button>
<div>{{obj1}}</div>
<h2>测试$set给对象新增属性</h2>
<button @click="setAttribute2">setAttribute2</button>
<div>{{obj2}}</div>
</div>
</template>
export default {
name: 'App',
data() {
return {
obj1: {name: 'kaizige', age: 18},
obj2: {name: 'wangrun', age: 81},
}
},
methods: {
setAttribute1() {
this.obj1.height = Math.random() * 100
},
setAttribute2() {
this.$set(this.obj2, 'height', Math.random() * 100)
},
}
}
这个例子是想给对象增加一个新的height属性,setAttribute1和setAttribute2方法分别是通过直接用"."操作符来添加属性和使用$set来添加属性,下面看动图:

很明显,直接给对象添加属性,vue是无法检测到的(响应失效了),但是$set可以。
点击
setAttribute2时obj1也发生了变化。这同样是由于错误的修改obj1的数据导致的,和上面的例子类似。
因此,对于对象属性的动态添加,必须使用$set方法。
总结
vue的响应式可以检测到已知属性的修改,对于新的属性,如果我们不通过$set方法去通知vue,vue是无法检测到的。这其中更深入的原因需要了解vue响应式的原理。