如今是时候深刻一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理很是简单直接,不过理解其工做原理一样重要,这样你能够回避一些常见的问题。在这个章节,咱们将进入一些 Vue 响应式系统的底层的细节。javascript
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data
选项,Vue 将遍历此对象全部的属性,并使用 Object.defineProperty 把这些属性所有转为 getter/setter。Object.defineProperty 是 ES5 中一个没法 shim 的特性,这也就是为何 Vue 不支持 IE8 以及更低版本浏览器。html
这些 getter/setter 对用户来讲是不可见的,可是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里须要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不一样,因此你可能须要安装 vue-devtools 来获取更加友好的检查接口。vue
每一个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程当中把属性记录为依赖,以后当依赖项的 setter
被调用时,会通知 watcher
从新计算,从而导致它关联的组件得以更新。java
受现代 JavaScript 的限制 (以及废弃 Object.observe
),Vue 不能检测到对象属性的添加或删除。因为 Vue 会在初始化实例时对属性执行 getter/setter
转化过程,因此属性必须在 data
对象上存在才能让 Vue 转换它,这样才能让它是响应的。例如:react
var vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应的 vm.b = 2 // `vm.b` 是非响应的
Vue 不容许在已经建立的实例上动态添加新的根级响应式属性 (root-level reactive property)。然而它可使用 Vue.set(object, key, value)
方法将响应属性添加到嵌套的对象上:git
Vue.set(vm.someObject, 'b', 2)
您还可使用 vm.$set
实例方法,这也是全局 Vue.set
方法的别名:github
this.$set(this.someObject,'b',2)
有时你想向已有对象上添加一些属性,例如使用 Object.assign()
或 _.extend()
方法来添加属性。可是,添加到对象上的新属性不会触发更新。在这种状况下能够建立一个新的对象,让它包含原对象的属性和新的属性:数组
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })` this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
声明响应式属性也有一些数组相关的问题,以前已经在列表渲染中讲过。浏览器
因为 Vue 不容许动态添加根级响应式属性,因此你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值:异步
var vm = new Vue({ data: { // 声明 message 为一个空值字符串 message: '' }, template: '<div>{{ message }}</div>' }) // 以后设置 `message` vm.message = 'Hello!'
这样的限制在背后是有其技术缘由的,它消除了在依赖项跟踪系统中的一类边界状况,也使 Vue 实例在类型检查系统的帮助下运行的更高效。并且在代码可维护性方面也有一点重要的考虑:data
对象就像组件状态的概要,提早声明全部的响应式属性,可让组件代码在之后从新阅读或其余开发人员阅读时更易于被理解。若是你未在 data 选项中声明 message
,Vue 将警告你渲染函数正在试图访问的属性不存在。
可能你尚未注意到,Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的全部数据改变。若是同一个 watcher 被屡次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免没必要要的计算和 DOM 操做上很是重要。而后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工做。Vue 在内部尝试对异步队列使用原生的 Promise.then
和 MessageChannel
,若是执行环境不支持,会采用 setTimeout(fn, 0)
代替。
例如,当你设置 vm.someData = 'new value'
,该组件不会当即从新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数状况咱们不须要关心这个过程,可是若是你想在 DOM 状态更新后作点什么,这就可能会有些棘手。虽然 Vue.js 一般鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,可是有时咱们确实要这么作。为了在数据变化以后等待 Vue 完成更新 DOM ,能够在数据变化以后当即使用 Vue.nextTick(callback)
。这样回调函数在 DOM 更新完成后就会调用。例如:
<div id="example">{{message}}</div>
var vm = new Vue({ el: '#example', data: { message: '123' } }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () { vm.$el.textContent === 'new message' // true })
Vue.component('example', {在组件内使用 vm.$nextTick()
实例方法特别方便,由于它不须要全局 Vue
,而且回调函数中的 this
将自动绑定到当前的 Vue 实例上:
template: '<span>{{ message }}</span>', data: function () { return { message: '没有更新' } }, methods: { updateMessage: function () { this.message = '更新完成' console.log(this.$el.textContent) // => '没有更新' this.$nextTick(function () { console.log(this.$el.textContent) // => '更新完成' }) } } })