Vue 2.0与3.0 的数据响应式实现 (待改进)

215 阅读3分钟

何为数据响应式

首先

const vm = new Vue({options}) //

当vm/options发生了变化(用户进行了互动)或(options内的变化-->使用props),用户/开发者都会得到-->响应,Vue 会通知到使用该数据的代码。

Vue将注入的组件/数据进行封装,当触发了相关的setter,则与之关联的组件/view会重新渲染-->数据和视图互相响应变化(对于UI控件来说)

!!Vue 在更新 DOM 时是异步执行的!!

Vue 2.0 响应式原理--Object.defineProperty

!Vue2.x! -官方文档--深入响应式原理

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

image.png

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

实际例子的体现

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。并且对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property

处理对象时

var vm = new Vue({ data:{ a:1}})     // `vm.a` 是响应式的 
vm.b = 2    // `vm.b` 是非响应式的

针对方法

Vue.set(vm.someObject, 'b', 2) //全局方法

this.$set(this.someObject,'b',2) //实例方法

处理数组时

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

第一种
利用索引直接设置一个数组项如:  vm.items[indexOfItem] = newValue
第二种
修改数组的长度如:  vm.items.length= newLength

针对方法

第一种
Vue.set(vm.items, indexOfItem, newValue) //全局方法

vm.items.splice(indexOfItem, 1, newValue)  //实例方法
vm.$set(vm.items, indexOfItem, newValue)   
第二种
vm.items.splice(newLength) 

声明响应式 property

var vm = new Vue({
  data: {
    // 声明 message 为一个空值字符串
    message: ''
  },
  template: '<div>{{ message }}</div>'
})
// 之后设置 `message`
vm.message = 'Hello!'

一个先声明占位的思想-->即使undefind也可以

在代码可维护性方面也有一点重要的考虑:data 对象就像组件状态的结构 (schema)。提前声明所有的响应式 property,可以让组件代码在未来修改或给其他开发人员阅读时更易于理解。


Vue2.0 总结

  • 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
  • Object.defineProperty 无法检测到对象属性的添加和删除 。
  • 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
  • 深度监听需要一次性递归,对性能影响比较大。

Vue 3.0 响应式原理--Proxy

!Vue3! -官方文档--深入响应式原理

当把一个普通的 JavaScript 对象作为 data 选项传给应用或组件实例的时候,Vue 会使用带有 getter 和 setter 的处理程序 遍历其所有 property 并将其转换为 Proxy-MDN

  • 当某个值发生变化时进行检测 不再需要-->Proxy 允许Vue拦截并且

  • 跟踪更改它的函数:在 proxy 中的 getter 中执行此操作,称为 effect

  • 触发函数更新最终值:proxy 中的 setter 中进行该操作,名为 trigger

实际例子的体现

Vue3.0 总结

  • 基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。
  • 不需要一次性遍历data的属性,可以显著提高性能。
  • Proxy是ES6新增的属性。