尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

《Vue3 从入门到大神33篇》Vue3 源码详解(三):响应式核心思想——从 Object.defineProperty 到 Proxy

《Vue3 从入门到大神33篇》Vue3 源码详解(三):响应式核心思想——从 Object.defineProperty 到 Proxy
📅 发布时间:2026/7/18 16:50:45

本篇关键词:响应式本质、Proxy、依赖收集、effect、track、trigger

难度等级:⭐⭐⭐⭐☆(偏底层,但全程带 Demo)

工程价值:读懂这一篇,你能手写简易状态管理库、精准定位响应式 Bug、面试秒杀 90% 候选人。


一、开篇:为什么 Vue3 要抛弃 Object.defineProperty?

如果你用过 Vue2,一定写过这样的代码:

data() { return { list: [1, 2, 3] } }

然后你发现:

this.list[0] = 99 // ❌ 不触发更新 this.list.length = 0 // ❌ 不触发更新 delete this.obj.a // ❌ 不触发更新

Vue2 的解决方案是:set、delete、重写数组方法。

这些不是“特性”,而是补丁。

Vue2 响应式的本质缺陷

Object.defineProperty只能做到:

  • ✅ 劫持已有属性

  • ❌ 无法监听新增 / 删除属性

  • ❌ 无法监听数组索引 / length

  • ❌ 需要递归遍历对象,性能差

👉结论:Vue2 的响应式是“后天修补出来的”。


二、Vue3 的解法:Proxy 带来的降维打击

Vue3 使用Proxy,一句话概括其优势:

Proxy 是对整个对象的拦截,而不是对属性的拦截。

Proxy 最直观的 Demo(先感性理解)

const obj = { count: 0 } const proxy = new Proxy(obj, { get(target, key, receiver) { console.log(`读取 ${key}`) return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { console.log(`修改 ${key} = ${value}`) return Reflect.set(target, key, value, receiver) } }) proxy.count // 读取 count proxy.count = 1 // 修改 count = 1

✅ 新增属性?能拦截

✅ 删除属性?能拦截

✅ 数组索引?能拦截

✅ 嵌套对象?懒代理(后面讲)


三、响应式系统的灵魂三问(源码级思维)

在阅读 Vue3 响应式源码前,必须先回答三个问题:

  1. 谁用了这个数据?(依赖收集)

  2. 数据变了通知谁?(依赖触发)

  3. 如何避免无限循环?(调度控制)

Vue3 的答案非常优雅:

概念

对应源码

依赖收集

track()

触发更新

trigger()

副作用函数

effect()


四、手写 Mini Reactive(VIP 核心环节)

不先写 Demo,直接读源码 = 劝退。

下面我们用不到 30 行代码,实现一个最简响应式系统。

Step 1:存储依赖的数据结构

// 存储所有副作用 const targetMap = new WeakMap() // 当前正在执行的 effect let activeEffect = null

设计说明:

  • WeakMap<target, Map<key, Set<effect>>>

  • 为什么用WeakMap?防止内存泄漏

  • 为什么用Set?防止重复收集


Step 2:track —— 依赖收集

function track(target, key) { if (!activeEffect) return let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } dep.add(activeEffect) }

一句话解释:

把“当前正在执行的副作用函数”,存到“目标对象的指定属性”下。


Step 3:trigger —— 触发更新

function trigger(target, key) { const depsMap = targetMap.get(target) if (!depsMap) return const dep = depsMap.get(key) if (dep) { dep.forEach(effect => effect()) } }

一句话解释:

找到“目标对象指定属性的所有副作用”,挨个执行。


Step 4:effect —— 副作用容器

function effect(fn) { activeEffect = fn fn() // 执行一次,触发 track activeEffect = null }

关键点:

  • 执行fn()时,内部读取响应式数据

  • 触发get→ 调用track

  • 完成依赖收集


Step 5:reactive —— 创建响应式对象

function reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { const result = Reflect.get(target, key, receiver) track(target, key) return result }, set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver) trigger(target, key) return result } }) }

Step 6:完整 Demo 跑起来 🎉

const state = reactive({ count: 0 }) effect(() => { console.log('count =', state.count) }) state.count++ // 控制台自动打印:count = 1 state.count = 5 // 控制台自动打印:count = 5

✅恭喜你,你已经理解了 Vue3 响应式的 80% 核心逻辑。


五、对照 Vue3 源码:你写的就是简化版源码

你现在写的代码,本质上就是:

packages/reactivity/src/ ├── effect.ts ← effect / track / trigger ├── reactive.ts ← reactive() └── baseHandlers.ts ← Proxy handlers

源码中的关键对应关系

手写

Vue3 源码

activeEffect

effectStack

track()

track()

trigger()

trigger()

deps

Dep类型

👉区别只是:源码多了调度器、递归代理、边界处理。


六、工程视角:为什么这样设计?(VIP 重点)

1️⃣ 为什么用 WeakMap?

WeakMap → key 必须是对象

好处:

  • 对象销毁 → 键值自动回收

  • 避免手动清理依赖(Vue2 的痛点)


2️⃣ 为什么 effect 要支持嵌套?

effect(() => { effect(() => { console.log(state.count) }) })

Vue3 使用effectStack​ 解决:

effectStack.push(effect) activeEffect = effect

3️⃣ 为什么需要调度器(Scheduler)?

试想:

state.count++ state.count++

你不希望触发两次更新。

Vue3 通过:

  • queueJob

  • nextTick

  • flushPostFlushCbs

实现异步批量更新。

(下一篇详细讲)


七、调试实战:在源码中验证你的理解

回到第二篇搭建的环境,在以下位置打断点:

packages/reactivity/src/effect.ts

重点关注:

  • track()何时被调用

  • trigger()如何遍历dep

  • activeEffect的变化过程

你会发现:

👉源码逻辑和你手写的 Mini Reactive 几乎一致。


八、面试高频追问(收藏级)

  1. Proxy 一定能监听到所有变化吗?

    不能。Map、Set需要单独处理(mutableHandlersvscollectionHandlers)

  2. Vue3 为什么还要 ref?

    Proxy 无法代理基本类型,ref本质是{ value }对象的语法糖。

  3. track 和 trigger 一定是同步的吗?

    不一定。计算属性、watch、组件更新都可能被调度延迟。


九、本篇总结

✅ Vue3 响应式 = Proxy + WeakMap + Effect

✅ 核心流程:读取收集 → 修改触发​

✅ 手写 Mini Reactive 是读源码的最短路径

✅ 工程中理解调度器、嵌套 effect、内存管理

一句话升华:

Vue3 的响应式不是“魔法”,而是一个设计极其精巧的观察者模式 + 代理模式组合。


十、下一篇预告

👉《Vue3 源码详解(四):reactive 与 ref 源码解析,搞懂响应式对象创建过程》

相关新闻

  • 芯片设计中的IP核与IP merge技术解析
  • 2026大同本地旅行社排名 深耕本土品牌综合实力盘点 - 资讯快报
  • 西安黄金回收区域测评:未央区VS碑林区避坑指数 - 日常财经早知道

最新新闻

  • 战略项目管理推荐指南:从规划到执行全场景选型全攻略 - 运营方法论
  • 鸿蒙 ArkTS 实战:Location Checkin Tool 从系统能力到状态管理完整解析
  • 电子灌封材料选型指南:环氧树脂、有机硅、聚氨酯与丙烯酸酯对比
  • 示波器核心原理与工程实践指南
  • RGB与MCU接口对比:LCD屏幕选型与性能优化
  • JESD204B协议在高速ADC同步中的关键技术与实践

日新闻

  • 宝珀中国官方售后服务中心|官方热线和维修地址权威信息声明(2026年7月更新) - 宝珀官方售后服务中心
  • # 2026年北京知识产权律师推荐怎么选?看这五点关键不踩雷 - 本地品牌推荐
  • 2026实测教程:生成的拼豆图纸不满意怎么修改才省事 - 省事研究所

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号