Vue2 的响应式

什么是响应式

Vue采用非入侵式响应式系统,数据模型仅仅是普通的JavaScript对象。
当修改数据时,视图会自动进行更新。

如何追踪变化

当把一个普通的JavaScript对象传入Vue实例,作为 data 选项。
Vue将遍历此对象的所有的property,并使用Object.defineProperty 把这些 property 全部转为 getter/setter。
在 property 被访问或者修改时,就会触发getter/setter,通知变更,让Vue能够追踪变化。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中,把“接触”过的数据的 property 记录为依赖。
之后,当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

核心API Object.defineProperty

const data = {}
const name = 'zhangsan'
Object.defineProperty(data, "name", {
  get: function () {
    console.log('get')
    return name
  },
  set: function (newVal) {
    console.log('set')
    return newVal
  }
})

// test
console.log(data.name) // get zhangsan
data.name = 'lisi' // set
  • 多层级对象需要用递归实现深度监听;

限制

由于JavaScript的限制,Vue不能直接检测数组和对象的变化。
需要用一些方法来回避限制,保证它们的响应式。

限制解决

对于对象

  • 由于在初始化时,进行getter/setter转化,所以无法检测 property 的添加/移除。
    • 单个 property
      Vue.set(Object, propertyName, value) / this.$set(Object, propertyName, value)
      例:
      Vue.set(vm.someObj, 'b', 2) / this.$set(this.someObj, 'b', 2)
    • 多个 property
      // 代替 Object.assign(this.someObject, { a: 1, b: 2 })
      this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

对于数组

  • Vue 不能检测以下数组的变动:
    • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    • 当你修改数组的长度时,例如:vm.items.length = newLength
      解决第一类问题
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// 或者
vm.$set(vm.items, indexOfItem, newValue)

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

解决第二类问题

vm.items.splice(newLength)

Object.defineProperty的缺点(Vue3.0启用Proxy)

  • 深度监听需要递归到底,一次性计算量大;
  • 无法监听 新增/删除 属性(Vue2解决用Vue.set / Vue.delete);
  • 无法原生监听数组,需要特殊处理(重写数组方法,在真正触发方法前,先更新视图)

Proxy的问题

  • 兼容性不好,而且无法polyfill
posted @ 2021-03-29 14:12  Better-HTQ  阅读(302)  评论(0编辑  收藏  举报