深入vue响应式原理

295 阅读2分钟

简介

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。让我们将研究一下 Vue 响应式系统的底层的细节。
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

注意点

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

对于对象

vue无法检测到对象的属性的增删,由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。如果不存在就无法检测变化;

var vm = new Vue({
  data:{
    obj:{
    a:1
  }
})

// `vm.obj.a` 是响应式的

vm.obj.b = 2
// `vm.obj.b` 是非响应式的

为了解决上面的问题,vue提供了Vue.set()或者vm.$set方法 Vue.set(vm.someObject, 'b', 2)

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

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

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

对于数组

Vue 不能检测以下数组的变动:

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

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

为了解决上面的问题,Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

这7个api会自动处理监听和代理,并更新UI,所以数组增删key最好通过这7个api