参考文章:深入响应式原理
一、如何追踪变化?
当你把一个普通的JS对象传入Vue实例作为data对象时,Vue将遍历此对象所有的property,并使用Object.defineProperty把这些property全部转为getter/setter。这些getter/setter对用户来说是不可见的,但是在内部它们让Vue能够追踪依赖,在property被访问和修改时通知变更。
每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把接触过的数据property记录为依赖。之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。
二、检测变化的注意事项
对于对象,Vue无法检测property的添加或删除,但是我们可以通过以下办法来保证响应性:
1.对于已经创建好的Vue实例,不允许动态添加根级别的响应式property。非根级别的,可以通过this.$set(object,propertyName,value)
方法添加,会响应式变化。
2.想给已有对象添加多个新property,并期望响应式变化。你应该这样写this.someObj = Object.assign({},this.someObj,{a:2,b:3})
,而不是Object.assign(this.someObj,{a:2,b:3})
对于数组,Vue无法检测以下变化:利用索引值直接设置数组项,vm.items[0]=2
;修改数组的长度,vm.items.length = 2
。那么应该怎么办?
1.设置数组项,可以这样写来保证响应,Vue.set(vm.items, indexOfItem, newValue)
或vm.items.splice(indexOfItem, 1, newValue)
。
2.修改数组长度,可以这样写,vm.items.splice(newLength)
三、声明响应式property
在初始化实例前声明所有根级响应式property,哪怕只是一个空值。
四、异步更新队列
Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件的循环中发生的所有数据变更。如果同一个watcher被多次触发,会在队列中去重,最终只执行一次。
当你设置vm.someDate = 'new Val'
,该组件不会立即重新渲染,而是会在下一个事件循环中才会更新DOM里的值。如果希望在DOM更新之后做些什么,可以有两种写法Vue.nextTick(callback)
或者await this.$nextTick()
。
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
或者
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}
网友评论