Vue的响应式原理和双向绑定

156 阅读3分钟

响应式原理

作用

Vue 的响应式可以实现数据和视图相关联,由数据驱动视图的更新

原理

Vue2的响应式原理

  • Vue2 的响应式原理依靠的是 Object.defineProperty() 方法
const obj={}
Object.defineProperty(obj, "b", {  //b为要定义或修改的属性键
  get() {  //访问属性时触发
    return bValue;
  },
  set(newValue) {  //修改属性时触发
    bValue = newValue;
  },
  enumerable: true,  //enumerable设置该属性是否在对象的属性枚举中出现
  configurable: true,  //configurable设置该属性是否可以被更改或者删除
});
  • 当在一个位置获取对象的属性 obj.b 时,会调用 getter 方法,数据会收集在一个集合中,当这个属性更新时,就可以通过这个集合获取到依赖这个属性的位置,并驱动视图更新
  • 当更新属性的值时,会触发 setter 方法,通知收集的依赖项更新,从而更新视图

Vue3 的响应式原理

  • Vue3 的响应式原理依靠的是 Proxy对象 ,Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
  • 与 Vue2 相比,Vue2 依靠的 Object.defineProperty ,无法监听数组基于下标的修改,也不支持 Map、Set 等,而 Proxy 解决了这些问题,并且可以代理整个对象。
  • 不论是在组件的 data 选项中返回一个普通的 js 对象,还是创建了一个 reactive 对象,Vue3 都会将其包裹在一个带有 get 和 set 处理程序的 Proxy 对象中
  • Vue3 中,通过 track 的处理器函数来收集依赖,通过 trigger 的处理器函数来派发更新,每个依赖的使用都会被包裹到一个副作用函数 effect 中,而派发更新后就会执行副作用函数,这样依赖的值就被更新了
function reactive(obj){
  return new Proxy(obj,{
    //get拦截对象属性的读取
    get(obj,prop,receiver){ //prop为待拦截属性名,receiver为proxy实例
      const value=Reflect.get(obj,prop,receiver);
       // 如果属性值是对象,递归地返回代理对象
      return typeof value === 'object' ? reactive(value) : value;
    },
    //set拦截对象的属性赋值操作
    set(obj,prop,value,receiver){ //value为新值
      const res=Reflect.set(obj,prop,value,receiver);
      return res;
    }
  })
}

双向绑定

作用

实现数据模型 Model 和视图 View 的同步关系,当其中一个改变,另一个也会同步更新,常用指令为 v-model

原理

先来看看 Vue 中的双向绑定的流程:

  1. new Vue() 执行初始化,对 data 执行响应化处理,这个过程发生在 Observe
  2. 编译模板,找到动态绑定的数据,从 data 中获取并初始化视图,这个过程发生在 Compile
  3. 同时定义一个更新函数和 Watcher ,数据变化时 Watcher 会调用更新函数
  4. 由于 data 的某个 key 在一个视图中可能出现多次,所以每个 key 都需要一个管家 Dep 来管理多个 Watcher
  5. data 数据变化时,会首先找到对应的 Dep,通知所有 Watcher 执行更新函数

依赖收集

视图中用到 data 的某个 key ,叫做依赖,同一个 key 可能出现多次,每次都需要收集出来用一个 Watcher 维护它们,此过程为依赖收集,多个 Watcher 需要一个 Dep 来管理,更新时由 Dep 统一通知

举例:

初始化视图时读取某个 key ,例如 name ,会创建一个 watcher ,由于触发了 name 的 getter 方法, watcher 会被添加到 name 对应的 Dep 中,当 name 更新时, setter 触发,就可以通过对应的 Dep 通知其管理的所有 watcher 更新,这样就实现了双向绑定

参考文章:vue3js.cn/interview/v…