Vue进阶 | 实现一个Vue2响应式

110 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

在 Vue2 中使用Object.defineProperty 来实现数据的响应式,在 Vue3 中使用 Proxy 来实现响应式(Proxy 是 ES6 中新增的功能)

Vue响应式

Vue 的双向绑定(响应式)是基于 MVVM 模型的。 MVVM 的特点是:数据变化后更新视图,视图变化后更新数据

  • Model 数据层
  • View 视图层
  • ViewModel 业务逻辑层,负责关联数据和视图

这个过程包含两个组成部分:

  • 监听器 Observer:对所有数据属性进行监听
  • 解析器 Compiler:对每个元素节点的指令进行扫描和解析,根据指令替换数据,绑定对应的更新函数

Object.defineProperty

采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的 settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调

image.png

代码实现(简版)

  • 循环对象中的每个数据,通过 Object.defineProperty 进行数据劫持
  • 通过递归实现深层的监听
  • typeof obj === 'object' 就是递归的条件(注:递归的是当前的val,不是当前obj)
  • 使用 getter 收集依赖 ,setter 通知 watcher派发更新
  • set() 中判断值是否改变,改变的话更新视图,如果未改变不更新视图
const render = (key, val) => {
    console.log(`key:${key},val:${val}`)
}
const defineReactive = (obj,key,val) => {
    reactive(val)
    Object.defineProperty(obj,key,{
        get() {
            return val
        },
        set(newVal) {
            if(val === newVal) return
            val = newVal
            render(key,val)
        }
    })
}
const reactive = (obj) => {
    if(typeof obj === 'object' && obj !== null) {
        for(const key in obj) {
            defineReactive(obj,key,obj[key])
        }
    }
}
const data = {
    a:1,
    b:2,
    c: {
        name: {
            firstName: 'Ha'
        },
        msg: 'hello'
    }
}

reactive(data)
data.a = 10
data.c.name.firstName = 'AAA'
data.c.msg = 'welcome!'

运行结果:

image.png

Proxy

Proxy 可以用来自定义对象中的操作。

Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,Proxy 可以监听到任何方式的数据改变。(第一种方法深度监听需要递归到底,性能层面相比不是特别好)