vue的双向数据绑定

发布于:2025-08-19 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、Vue2 的响应式原理

1. 底层依赖:Object.defineProperty

  • Vue2 在 initData 阶段,会遍历 data 对象,用 Object.defineProperty 给每个属性加上 getter / setter

  • getter:依赖收集 → 当模板渲染读取属性时,把当前的渲染函数 Watcher 收集到依赖中。

  • setter:派发更新 → 当数据变化时,通知 Dep 里收集的 Watcher 重新执行更新。

2. 特点

  • 能拦截 对象已有属性 的读写。

  • 对于 新增属性 / 删除属性 无法检测,只能用 Vue.set / Vue.delete

  • 数组 的监听有限:重写了数组的 7 个变更方法(push、pop、shift、unshift、splice、sort、reverse),但无法监听通过下标直接修改数组元素。

二、Vue3 的响应式原理

1. 底层依赖:Proxy

  • Vue3 使用 Proxy 包裹整个对象,统一拦截 对象所有操作(读、写、删、in 判断、遍历等)。

  • 可以拦截对象的 内部方法(内部方法 = ECMAScript 规范里的 [[Get]] [[Set]] [[Delete]] [[HasProperty]] 等)

2. 常见拦截方法

  • get(target, key, receiver) → 依赖收集。

  • set(target, key, value, receiver) → 派发更新。

  • deleteProperty(target, key) → 删除属性时触发更新。

  • has(target, key)in 操作符拦截。

  • ownKeys(target)for...inObject.keys 拦截。

3. 特点

  • 可以监听 新增 / 删除属性

  • 可以监听 数组下标修改

  • 能对 MapSet 等原生集合类型进行响应式处理。

  • 不需要像 Vue2 那样“递归遍历所有属性”,只有在真正访问属性时才递归代理(懒代理)。


🔹 三、框架应用层面

  1. Vue2 (defineProperty)

    • 只能拦截对象已有属性的 读写

    • 其他内部操作(比如 indelete、遍历)拦截不了。

  2. Vue3 (Proxy)

    • 可以拦截对象的 所有基本操作,包括读写、删除、判断、遍历。

    • 响应式系统更健壮、能力更强。


🔹 四、Vue2 vs Vue3 对比总结

对比点 Vue2 (Object.defineProperty) Vue3 (Proxy)
拦截范围 仅限属性读写 支持读写、删除、in、遍历等所有操作
新增/删除属性 不能监听,需 Vue.set / Vue.delete 原生支持
数组监听 只能通过重写方法,不能拦截下标修改 可直接监听下标修改
嵌套对象 初始化时递归遍历,性能差 访问时懒代理,性能好
Map/Set 不支持 支持
性能 初始化慢(深度递归) 初始化快(按需代理)

五、面试回答示例(结合应用层+底层原理)

“在 Vue2 里,响应式是通过 Object.defineProperty 来实现的,本质上是给对象已有属性添加 getter/setter,用来做依赖收集和派发更新。但它有局限,比如不能监听新增/删除属性,对数组也只能通过方法重写来监听。

Vue3 则是基于 Proxy 实现的,它可以拦截对象的所有内部操作,比如 get/set/delete/has/ownKeys 等,所以不仅能监听属性读写,还能监听新增删除、数组下标修改、for...in 遍历等操作。同时 Vue3 对象是懒代理,只有在访问时才递归代理,性能更优,也支持 Map/Set 这类新数据结构。

所以从框架应用层面看,Vue2 的双向数据绑定只能拦截‘现有属性的读写’,而 Vue3 则能拦截对象的基本方法/内部方法,响应式能力更强。”

  • 左边是 Vue2 响应式原理(基于 Object.defineProperty

  • 右边是 Vue3 响应式原理(基于 Proxy

可以清晰看到:

  • Vue2 需要遍历属性,getter 收集依赖、setter 派发更新;

  • Vue3 直接通过 Proxy 拦截各种操作,依赖收集和派发更新都集中到 Effect


笔试手写:

🔹 Vue2 响应式原理 (Object.defineProperty)

 data
  ↓
 Object.defineProperty(遍历每个属性)
  ↓
 ┌─────────── getter ───────────┐
 │ 读取数据 → 依赖收集 (Dep)     │
 └──────────────────────────────┘
 ┌─────────── setter ───────────┐
 │ 修改数据 → 通知 Watcher 更新 │
 └──────────────────────────────┘
  ↓
  视图更新

👉 关键点:只能拦截“已有属性”,新增/删除属性和数组下标改动监听不到。

Vue3 响应式原理 (Proxy)

 data
  ↓
 Proxy(代理整个对象)
  ↓
 ┌─────────── get ───────────┐
 │ 读取属性 → 依赖收集 (Effect)│
 └────────────────────────────┘
 ┌─────────── set ───────────┐
 │ 修改属性 → 触发更新         │
 └────────────────────────────┘
 ┌──────── delete/has/keys ──┐
 │ 删除/判断/遍历 → 可拦截    │
 └────────────────────────────┘
  ↓
  组件更新
关键点:能拦截几乎所有操作(新增/删除/遍历/数组下标/Map/Set 等

🔹 面试小技巧

  • 如果面试官要你“画个图说明”,就用这个 ASCII 简版,既能表现出流程,也直观体现了 Vue2/3 的区别。

  • 如果他继续问深一点(比如 Dep / Effect 是怎么实现的),就说 Vue 内部通过 依赖收集容器(Dep/ReactiveEffect) 把响应式数据和视图更新关联起来,数据变 → 通知依赖 → 触发渲染。


网站公告

今日签到

点亮在社区的每一天
去签到