深刻vue响应式原理(day03)

VUE是如何追踪变化的

当你把一个普通的 JavaScript 对象传入 Vue 实例做为 data 选项,Vue 将遍历此对象全部的 property,并使用 Object.defineProperty 把这些 property 所有转为 getter/setter。Object.defineProperty 是 ES5 中一个没法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的缘由。

这些 getter/setter 对用户来讲是不可见的,可是在内部它们让 Vue 可以追踪依赖,在 property 被访问和修改时通知变动。这里须要注意的是不一样浏览器在控制台打印数据对象时对 getter/setter 的格式化并不一样,因此建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

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

检测变化的注意事项

因为 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此咱们仍是有一些办法来回避这些限制并保证它们的响应性。

对于对象

对于已经建立的实例,Vue 不容许动态添加根级别的响应式 property。可是,可使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于

Vue.set(vm.someObject, 'b', 2)

您还可使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

this.$set(this.someObject,'b',2)

有时你可能须要为已有对象赋值多个新 property,好比使用 Object.assign() 或 _.extend()。可是,这样添加到对象上的新 property 不会触发更新。在这种状况下,你应该用原对象与要混合进去的对象的 property 一块儿建立一个新的对象。

对于数组

Vue 不能检测如下数组的变更:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一类问题,如下两种方式均可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

声明响应式 property

因为 Vue 不容许动态添加根级响应式 property,因此你必须在初始化实例前声明全部根级响应式 property,哪怕只是一个空值:

若是你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问不存在的 property。
这样的限制在背后是有其技术缘由的,它消除了在依赖项跟踪系统中的一类边界状况,也使 Vue 实例能更好地配合类型检查系统工做。
但与此同时在代码可维护性方面也有一点重要的考虑:data 对象就像组件状态的结构 (schema)。提早声明全部的响应式 property,可让组件代码在将来修改或给其余开发人员阅读时更易于理解。

异步更新队列

可能你尚未注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的全部数据变动。若是同一个 watcher 被屡次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免没必要要的计算和 DOM 操做是很是重要的。而后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工做。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,若是执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

例如,当你设置 vm.someData = 'new value',该组件不会当即从新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数状况咱们不须要关心这个过程,可是若是你想基于更新后的 DOM 状态来作点什么,这就可能会有些棘手。虽然 Vue.js 一般鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,可是有时咱们必需要这么作。为了在数据变化以后等待 Vue 完成更新 DOM,能够在数据变化以后当即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。

相关文章
相关标签/搜索