我对Vue数据响应式的理解

555 阅读2分钟
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的js对象,当我们对其进行修改时,视图会进行更新。接下来我们来了解其中的工作原理。

什么是响应式?

如果一个物体能对外界的刺激做出反应,它就是响应式的。

Vue的数据(data)是响应式的

const vm = new Vue({data:{n:0}})

如果我们修改vm.n(vm是data对象的代理),那么UI中的n就会响应我,Vue2通过Object.defineProperty来实现数据响应式。

如何追踪变化

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

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

这些getter/setter对用户来说是不可见的,但是内部他们让Vue能够追踪依赖,在property被访问和修改时通知变更。

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

检测变化的注意事项

对于对象

Vue对于对象的property的添加或移除无法检测。由于Vue会在初始化实例对property执行getter/setter转化,所以property必须在data对象上存在才能让Vue将它转换成响应式的。比如:

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

vm.a=2
// `vm.a` 是响应式的

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

可以使用Vue.set(object,propertyName,value)/vm.$set(object,propertyName,value)来添加响应式property。

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

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
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 // 不是响应性的

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

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

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice

vm.items.splice(newLength)

可以用的vueArray的APi有:

push()//在数组后面添加元素
pop()//在数组后面删除元素
shift()//在数组前面删除元素
unshift()//在数组前面添加元素
splice()//删除或替换数组元素
sort()//默认把数组从小到大排序
reverse()//把数组顺序调换



参考:深入响应式原理