如何追踪变化
1. Object.defineProperty 把属性转为 getter/setter,用以检测数据的变化。
- 当把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property全部转为 getter/setter。
- get 是一个函数,当属性被访问时,会触发 get 函数
- set 也是一个函数,当属性被赋值时,会触发 set 函数,例如
var obj={
name:"响应式"
}
Object.defineProperty(obj,"name",{
get(){
console.log("get 被触发")
},
set(val){
console.log("set 被触发")
}
})
- 当访问 obj.name 时,会打印 ' get 被触发 '
- 当给 obj.name 赋值时,obj.name = 'xxx',会打印 ' set 被触发 '
2. watcher ,把组件渲染接触过的数据属性记为依赖
- 每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化,解决方法是手动调用 Vue.set 或者 this.$set
1. 检测对象的变化
- 对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。
var vm = new Vue({
data:{
a:1
}
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的
- 使用 Vue.set或者vm.$set (object, propertyName, value) 方法向嵌套对象添加响应式属性
Vue.set(vm.someObject, 'b', 2) //Vue.set
this.$set(this.someObject,'b',2) //vm.$set 实例方法,这也是全局 Vue.set 方法的别名
- 有时可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
2. 检测数组的变化
- 当你利用索引直接设置一个数组项和修改数组的长度时,Vue 不能检测到数组的变动:
- 例如:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
- 利用索引直接设置一个数组项可以用对象的方式(Vue.set或者vm.$set (object, propertyName, value) ),也可以用splice方法
Vue.set(vm.items, indexOfItem, newValue) //Vue.set
vm.$set(vm.items, indexOfItem, newValue) //vm.$set
vm.items.splice(indexOfItem, 1, newValue) //splice
- 修改数组的长度也可以用splice,splice在只有一个参数的时候表示保留的数组的长度
vm.items.splice(newLength)
异步更新队列
- Vue 在更新 DOM 时是异步执行的
- 只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次(会在缓冲时去除重复数据)
- 所以如果想要在数据更新之后拿到数据,最好使用
Vue.nextTick(callback)
或vm.$nextTick()
实例方法