首先,我们得明白Vue数据响应的原理:
以对象为例:当把一个JavaScript对象传给Vue实例的data选项时,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,
而getter/setter可以做到追踪依赖,在属性被访问和修改时通知变化。
那么,什么是getter/setter ?
var person = {
defaultname: 'tom',
get name() {
return this.defaultname
},
set name(val) {
this.defaultname = val
console.log("触发了set")
}
}
person.name =>"tom"
person.name = 123 =>触发了set
person.name =>123
getter/setter其实就是get、set方法,set能检测到数据的改变并进行操作。
但在有些情况下setter不能检测到数据的变化,即没有触发set。
例如:
person.name = {name: 'tom'}
=>触发了set
person.name
=>{name: "tom"}
person.name.name = 123123
=>没有打印出任何信息,说明修改name的属性值并没有触发set方法。
person.name.sex = 333
=>没有打印出任何信息,说明添加name的属性值并没有触发set方法。
delete person.name.name
=>没有打印出任何信息,说明删除name的属性值并没有触发set方法。
Object.defineProperty(person.name, 'name', {value: 4444})
=>没有打印出任何信息,说明Object.defineProperty也没有触发set方法。
追踪依赖在这种情况下并没有形成,这时数据绑定失效的根本原因。
失效场景总结:
对象
修改、添加、删除对象的属性值
数组
设值,修改长度,push、pop、shift、unshift等原生方法
------------------------------------------------------------------------
解决方案
对象
单个属性:可使用Vue.set(object, key, value)方法将响应属性添加到嵌套的对象上
多个属性:替换方法,即:创建一个新的对象赋值给原对象
如:this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
数组
Vue提供的对原生方法修改的变异方法:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
非变异方法(替换方法):(本质上也是创建一个新的数组赋值给原数组)
如:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
那么这儿就有一个思考,替换方法是否低效?是否会导致dom树的重新渲染?
不用担心,大胆用!因为Vue为了使得 DOM 元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
-----------------------------------------------------------------------------------------
另外,还有一种失效的场景,即给data对象添加属性时失效,这里得声明:
初始化时才会对属性执行getter/setter转化过程,所以属性必须在data对象上存在,或者设置为某个对象的属性。