Vue3 手写响应式原理

Vue3 手写响应原理

一、概念

  1. Vue 3 响应式核心原理
    Vue 3 抛弃 Vue 2 的Object.defineProperty,基于Proxy + Reflect实现响应式,核心逻辑:
  • 拦截操作:通过 Proxy 拦截对象的get(读)/set(写)操作;
  • 依赖收集:读取响应式数据时(get),记录 "副作用函数 - 数据" 的映射关系;
  • 触发更新:修改响应式数据时(set),执行该数据关联的所有副作用函数。
  1. Proxy和Object.defineProperty对比
维度 Object.defineProperty(Vue 2) Proxy + Reflect(Vue 3 )
支持类型 仅对象 / 数组(需特殊处理) 支持所有复杂类型(Object/Array/Map 等)
拦截能力 仅拦截属性读写 拦截 13 种操作(读写 / 删除 / 遍历等)
深层响应式 递归到底(初始化性能差) 惰性拦截(访问时创建代理,性能优)
新增 / 删除属性 无法拦截(set, delete) api原生支持
  1. Reflect 的核心作用
  • 标准化操作行为:将objkey封装为Reflect.get(obj, key),返回值更规范;
  • 保留上下文:保证 Proxy 拦截时this指向原始对象(原始对象get/set访问this指向);
  • 与 Proxy 一一对应:13 种拦截方法完美适配。

二、手写代码核心逻辑

js 复制代码
class Depend {
    constructor(){
        this.effectFns = new Set()
    }
    track(){
        this.effectFns.add(dependCollection.dependCallback)
        dependCollection.dependCallback = null
    }
    trigger(){
        if(this.effectFns.size <= 0) return
        for(let fn of this.effectFns){
            fn?.()
        }
    }
}

class CollectionOfDepend {
    _dependWeakMap = null
    dependCallback = null
    constructor(){
        this._dependWeakMap = new WeakMap()
    }
    trackDepend(target, key){
        let dependMap = this._dependWeakMap.get(target)
        if(!dependMap){
            dependMap = new Map()
            this._dependWeakMap.set(target, dependMap)
        }
        let depend = dependMap.get(key)
        if(!depend){
            depend = new Depend()
            dependMap.set(key, depend)
        }
        depend.track()
    }
    trigger(target, key){
        this._dependWeakMap.get(target)?.get?.(key)?.trigger?.()
    }
}

const dependCollection = new CollectionOfDepend()

const isDeepMap = (value) => {
    if(value === null) return false
    return (typeof value === 'object') || Array.isArray(value)
}

const reactive = (proxyObj) => {
    return new Proxy(proxyObj, {
        get(target, key, receiver){
            const value = Reflect.get(target, key, receiver)
            // 对象继续深层遍历
            if(isDeepMap(value)){
                return reactive(value)
            }
            dependCollection.trackDepend(target, key)
            return value
        },
        set(target, key, value , receiver){
            const oldValue = Reflect.get(target, key)
            Reflect.set(target, key, value, receiver)
            // value没有改变时不触发
            if(oldValue === value) return
            dependCollection.trigger(target, key)
        }
    })
}

const userInfo = reactive({
    name: '刘德华',
    age: 18,
    sex: '男',
    friend: {
        name: '小孔'
    }
})

const watchEffect = (initinalFn) => {
    dependCollection.dependCallback = typeof initinalFn === 'function' ? initinalFn : () => {}
    try {
        initinalFn()
    } catch (error) {
        throw new Error(error)
    } finally {
        dependCollection.dependCallback = null
    }
}

watchEffect(() => {
    console.log('watchEffect1', userInfo.name)
})

watchEffect(() => {
    console.log('watchEffect2', userInfo.age)
})

watchEffect(() => {
    console.log('watchEffect3', userInfo.name, userInfo.age)
})

watchEffect(() => {
    console.log('watchEffect4', userInfo.friend.name)
})

// userInfo.name = '张学友'
userInfo.friend.name = '小明'

// 打印结果 --------------------------------------------
// watchEffect1 刘德华
// watchEffect2 18
// watchEffect3 刘德华 18
// watchEffect4 小孔
// watchEffect4 小明

三、总结

Vue3 抛弃了 Vue2 中的旧响应系统,通过 Proxy 对数据进行劫持,并且通过发布订阅模式对每个需要监听的对象进行了依赖收集 (get属性),再使用了Proxy 的 set 属性来触发依赖,相比使用之前 defineProperty并递归遍历对象,性能得到了极大的提升。

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A3 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
honkun63 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19913 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端