Skip to content
内容编写中

Vue2 的数据劫持机制

Vue2 的响应式系统基于 ​Object.defineProperty​ 实现,核心步骤如下:

  1. ​ 属性遍历与劫持 ​: 初始化时递归遍历 data对象的每个属性,通过 Object.defineProperty 将其转换为带有 getter/setter 的访问器属性。在 getter 中收集依赖(如组件的渲染函数),在 setter 中触发更新。
  2. 依赖收集与派发更新 ​: 每个属性对应一个 Dep 类实例(依赖管理器),当属性被访问时,当前 Watcher(订阅者)会被添加到 Dep 的订阅列表中;当属性修改时,Dep 会通知所有 Watcher 执行更新逻辑。
  3. 局限性 ​:
  • **​ 数组处理不足 ​:**无法直接监听数组索引变化(如 arr[0] = 1)和 length 修改,需重写数组的 pushpop 等方法。
  • **​ 动态属性监听缺失 ​:**无法自动响应新增或删除的属性,需通过 Vue.setVue.delete 手动触发。

Vue3 的数据劫持机制

Vue3 采用 ​Proxy​ 替代 Object.defineProperty,实现更高效的响应式系统:

  1. ​ 代理对象与拦截操作 ​: 使用 Proxy 创建目标对象的代理,通过 getset 拦截器捕获属性的访问和修改。Proxy 支持拦截所有操作(包括新增/删除属性、数组索引变化等),无需遍历属性。
  2. 深度响应式处理 ​: 当访问嵌套对象时,自动递归将其转换为响应式代理,避免初始化时的性能损耗。 3.** ​ 依赖追踪优化 ​:** 引入 effect 函数和 track/trigger 机制,通过 WeakMapMap 结构动态追踪依赖关系,减少内存占用并提升性能。
  3. 优势 ​:
  • **全面监听 ​:**支持数组、动态属性、Map/Set 等复杂数据结构。
  • **惰性代理 ​:**仅在访问时处理嵌套对象,避免不必要的递归。

Vue2 与 Vue3 数据劫持的对比

对比维度Vue2 (Object.defineProperty)Vue3 (Proxy)
实现方式遍历对象属性,通过 Object.defineProperty 逐个劫持,需递归初始化所有属性通过 Proxy 代理整个对象,拦截所有操作,按需(惰性)递归处理嵌套对象
数据劫持能力❌ 无法监听数组索引修改和 length 变化
❌ 无法自动监听动态新增/删除属性
✅ 支持数组索引修改和 length 变化
✅ 支持动态新增/删除属性
性能⚠️ 初始化时递归遍历所有属性,性能损耗较大✅ 按需劫持,访问时递归代理嵌套对象,内存占用更低
兼容性✅ 支持 IE9+❌ 不支持 IE(依赖 ES6 Proxy)
API 设计基于 Dep 类和 Watcher 类实现依赖收集与派发更新基于 effect 副作用函数和 track(追踪依赖)/trigger(触发更新)函数实现响应式
复杂数据结构支持❌ 不支持 Map/Set/WeakMap✅ 支持 Map/Set/WeakMap 等 ES6+ 数据结构
代码维护性需要手动处理数组方法和动态属性,逻辑分散统一通过 Proxy 拦截操作,代码更集中且可扩展性强

总结

  • ​Vue2​ 的 Object.defineProperty 机制简单但存在性能瓶颈和监听盲区,需通过额外方法弥补 。
  • ​Vue3​ 的 Proxy 提供了更高效、全面的响应式支持,尤其适合复杂应用场景。 开发者可根据项目需求(如兼容性、性能)选择版本,Vue3 的响应式系统在灵活性和效率上显著领先

Released under the MIT License.