# 深入响应式原理

defineProperty

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty (opens new window) 把这些 property 全部转为 getter/setter

由于Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

watcher

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

# 保证数据响应式

  • 对于对象

property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的

var vm = new Vue({
  data: {
    a: 1
  }
});
// `vm.a` 是响应式的
vm.b = 2;
// `vm.b` 是非响应式的
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 });
  • 对于数组

Vue 重写了数组的操作方法,调用这些方式时,可以进行消息通知,从而更新

var vm = new Vue({
  data: {
    items: ["a", "b", "c"]
  }
});
vm.items[1] = "x"; // 不是响应性的
vm.items.length = 2; // 不是响应性的
vm.$set(vm.items, indexOfItem, newValue); //是响应式的
vm.items.splice(newLength); //是响应式的

# 异步更新队列

Vue 在更新 DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一循环中发生的所有数据变更。

当然如果同一个 watcher 被多次触发,只会被推入到队列中一次。然后在下一个事件循环“tick"中,Vue 刷新队列并执行实际工作。 所以不用担心 Watch 出现死循环的问题。

为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用Vue.nextTick(callback).