监听变化
Vue实例中的数据保存在该实例的data属性中,当数据对象传入data中时,Vue会遍历该对象所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter,在属性被访问(get)时调用getter获取属性值。
Vue实例中每个组件都有对应的watcher对象,在组件渲染的过程中watcher对象把组件中的属性记录为依赖,当依赖的属性修改(set)时调用setter通知watcher对象,从而触发re-render重新计算组件进而更新virtual DOM树。
注意事项
Vue 实例是在初始化(create)阶段将属性转化为getter/setter的
(在mount阶段为组件添加watcher),只有在初始化之前已经存在于data对象的属性才能被Vue转化并实现响应,初始化之后添加和删除的对象属性无法被检测到。
var vm = new Vue({
data:{
a:1
}
})
// vm.a 追踪变化
vm.b = 2
// vm.b 不追踪变化
Vue无法在data对象中添加响应属性,如果有需要添加的响应属性必须在data对象声明,即使是空值。
如果在html中使用了未声明的数据属性,Vue会抛出错误,视图中 {{ }} 会视该属性为空 ,v-if v-show会判断为fasle。
var vm = new Vue({
data: {
a:1,
b:'',
}
})
vm.b = 2// 追踪变化
Vue实例中data对象内部可以添加响应属性。
Vue提供了Vue.set(object, key, value)
方法用来添加要响应属性到data对象内部。
vm.a.a = 1
//vm.a.a 为1 不追踪变化
Vue.set(vm.a, 'b', 2)
//vm.a.b 为2 追踪变化
或
this.$set(this.a,'b',2)
//vm.a.b 为2 追踪变化
区别:vm.$set为
实例方法, Vue.set为
全局方法。
使用 Object.assign()
方法来添加到对象上的新属性由于没有转化getter/setter,故同样不会触发更新。
可以通过创建一个包含原对象属性和新属性的新对象,并赋值给原对象来实现添加响应属性。
//Object.assign(this.a, { c: 1, d: 2 })
//vm.a.c 为1 vm.a.d 为2 不会追踪变化
this.a = Object.assign({}, this.a, { c: 1, d: 2 })
//vm.a.c 为1 vm.a.d 为2 追踪变化
这里相当于利用了data对象对data内部属性的监听响应来实现data内部属性的更新,由于a是data对象的属性且a可以追踪变化,所以当a被重新赋值,其内部所有属性都会转化getter/setter,故可以被追踪变化。
DOM更新
Vue 使用异步方式执行 DOM 更新。只要观察到数据变化,Vue会将处在当前事件循环中发生的所有数据改变缓存起来,并开启一个队列,如果同一个 watcher 被多次触发,只会被推入到队列中一次,从而实现去除重复数据,避免不必要的计算和 DOM 操作。在下一个事件循环“tick”中,Vue 刷新队列并执行(已去重的) 工作。
Vue 在内部尝试对异步队列使用原生的 Promise.then
和 MessageChannel
,如果执行环境不支持,会采用 setTimeout(fn, 0)
代替。
鉴于Vue对DOM异步更新的特性,在数据改变之后立刻进行DOM操作则该DOM元素会处于未更新状态,如果要在Vue 完成DOM更新之后进行DOM操作,则要在数据变化之后立即使用 Vue.nextTick(callback)
方法,callback回调函数将在在 DOM 更新完成后调用。
var vm = new Vue({
el: '#ele',
data: {
a: 1
}
})
vm.a = 2 // 更改数据
vm.$el.textContent === 2 // false
Vue.nextTick(function () {
vm.$el.textContent === 2 // true
})
在组件内使用 vm.$nextTick()
回调函数中的 this
将自动绑定到当前的 Vue 实例上
Vue.component('tpl', {
template: '<span>{{ a }}</span>',
data: function () {
return {a:1}
},
methods: {
tplUpdateData: function () {
this.a = 2
console.log(this.$el.textContent) // => '没有更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '更新完成'
})
}
}
})
区别:vm.$nextTick为
实例方法, Vue.nextTick为
全局方法。