基本认识
- 作用:Vue 会递归将 data 的属性转换为 getter/setter 方法,从而使得 data 中属性能够响应数据变化。
- 类型: Object | Function
// Object 类型 data 属性
const vm = new Vue({
data: {
name: 'IT白'
}
})
// Function 类型 data 属性
const component = Vue.extend({
template: '<p>My name is {{ name }}</p>',
data: function () {
return {
name: 'IT白',
}
}
})
创建 Object 类型与 Function 类型的data属性的异同
- 相同点:Vue 都会为其生成相应的 getter/setter 方法。
- 不同点:当组件被同一时间多次实例化时
Object 类型:组件的所有实例都会引用同一个数据对象。当一个实例修改的自身的 data 属性,由于引用统一个数据对象,导致修改也将会在其它实例中呈现。
Function 类型:每次创建组件实例时, Vue 都将通过调用该函数,创建一个全新副本的数据对象。当一个实例修改自身的 data 属性,由于每个实例中的 data 属性对象都是单独的副本,所以不会影响其它实例。
注:如果将组件的 data 属性定义为 Object 类型,Vue 会抛出警告。
data 属性响应式原理
追踪数据变化
- 准备:Vue 会遍历 data 对象(如果是 Function 类型,Vue 会先调用函数,创建对应对象),并将所有属性通过
Object.defineProperty()
转换成getter/setter
。 - collect as dependency:每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。
- notify:当 setter 被触发时,会通知 watcher。
- trigger re-render:watcher 重新渲染关联的组件。
- touch:如果实例修改 data 属性的值,将会触发 setter。
Vue 官方文档插图
检测变化的注意事项
由于 JavaScript 的限制,Vue 无法检测对象属性的添加或删除,所以直接动态添加属性到 data 对象中,该属性为非响应式。
var vm = new Vue({
data:{
a:1
}
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的
但是可以通过 Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式属性,以及 Object.assign()
方法创建一个新的对象。
Vue.set(vm.someObject, 'b', 2)
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
异步更新队列
Vue 在更新 DOM 时是异步执行的。
只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
Vue 在内部对异步队列尝试使用原生的Promise.then
、MutationObserver
和setImmediate
,如果执行环境不支持,则会采用setTimeout(fn, 0)
代替。使用
.nextTick()
。
为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用Vue.nextTick(callback)
。这样回调函数将在 DOM 更新完成后被调用。