Vue 的双向数据绑定(v-model)核心是实现 Model(数据)与 View(视图)的自动同步:当数据(Model)变化时,视图(View)自动更新;当用户操作视图(如输入、选择)时,数据(Model)自动同步。Vue2 和 Vue3 虽然底层实现有所差异,但核心流程一致,以下分版本解析其原理。

一、Vue2 的双向数据绑定原理

Vue2 基于 Object.defineProperty 实现响应式系统,结合指令系统完成双向绑定,核心涉及 响应式数据劫持依赖收集视图更新 和 事件监听 四个环节。

1. Model → View:数据变化驱动视图更新
  • 步骤 1:数据劫持(响应式初始化)当组件初始化时,Vue 会对 data 中的属性通过 Object.defineProperty 进行劫持,重写 getter 和 setter

    • getter:当数据被访问时,触发依赖收集(记录使用该数据的 “Watcher”)。
    • setter:当数据被修改时,触发依赖通知(通知所有关联的 “Watcher” 执行更新)。
  • 步骤 2:依赖收集(关联数据与视图)组件渲染时,模板中的指令(如 v-model)或插值表达式会访问数据,触发 getter。此时 Vue 会创建一个 “渲染 Watcher”,并将其加入数据对应的 “依赖列表(Dep)” 中,完成数据与视图的关联。

  • 步骤 3:数据变化触发视图更新当 Model 数据被修改(如 this.msg = 'new'),会触发 setter,进而会调用 Dep.notify() 通知所有关联的 Watcher。Watcher 会触发组件重新渲染(执行 render 函数生成新虚拟 DOM,通过 diff 算法更新真实 DOM),最终视图同步更新。

2. View → Model:视图操作同步数据更新

v-model 本质是语法糖,会自动为表单元素绑定 input 事件(或其他事件,如复选框的 change),并将事件回调中的值同步到数据。以输入框为例:

html

预览

<input v-model="msg">

编译后等价于:

html

预览

<input :value="msg" @input="msg = $event.target.value">
  • 步骤 1:视图操作触发事件用户在输入框中输入内容时,会触发 input 事件(浏览器原生事件)。

  • 步骤 2:事件回调同步数据Vue 为该事件注册的回调函数会将事件对象中的值($event.target.value)赋值给 msg,直接修改 Model 数据。

  • 步骤 3:数据变化再次驱动视图(闭环)由于 msg 是响应式数据,修改后会触发 setter,再次进入 “Model → View” 的更新流程(确保视图与数据最终一致,避免手动修改数据后视图不一致)。

二、Vue3 的双向数据绑定原理

Vue3 改用 Proxy 实现响应式系统,同时优化了编译逻辑和事件处理,但双向绑定的核心流程(Model 与 View 同步)与 Vue2 一致,差异主要体现在响应式底层实现和编译优化。

1. Model → View:数据变化驱动视图更新
  • 步骤 1:数据代理(基于 Proxy)Vue3 通过 Proxy 对 reactive 或 ref 包装的数据进行代理(而非 Object.defineProperty),拦截数据的读取(get)和修改(set):

    • get 拦截:当数据被访问时,触发依赖收集(通过 track 函数记录 “副作用函数”)。
    • set 拦截:当数据被修改时,触发依赖更新(通过 trigger 函数执行所有关联的副作用函数)。
  • 步骤 2:依赖收集(副作用函数关联)组件渲染函数会被包装为 “副作用函数(effect)”,当渲染时访问数据(如 v-model 绑定的数据),get 拦截器会调用 track 函数,将该副作用函数与数据关联(存储于 targetMap 中)。

  • 步骤 3:数据变化触发视图更新当 Model 数据被修改(如 msg.value = 'new'),set 拦截器会调用 trigger 函数,找到关联的副作用函数(渲染函数)并执行。渲染函数生成新虚拟 DOM,结合 PatchFlags 精准更新真实 DOM,完成视图同步。

2. View → Model:视图操作同步数据更新

Vue3 中 v-model 的语法糖逻辑与 Vue2 类似,但编译时会生成更高效的代码,且支持自定义组件的双向绑定优化(通过 modelValue 和 update:modelValue 事件)。以输入框为例:

html

预览

<input v-model="msg">

编译后等价于:

html

预览

<input :value="msg" @input="msg = $event.target.value">
  • 步骤 1:视图操作触发事件用户输入时触发 input 事件,与 Vue2 一致。

  • 步骤 2:事件回调同步数据事件回调直接修改响应式数据(如 msg 为 ref 时,实际修改 msg.value),触发 Proxy 的 set 拦截器。

  • 步骤 3:数据变化驱动视图更新set 拦截器通过 trigger 函数触发渲染副作用函数,重新渲染并更新视图,形成闭环。

三、Vue2 与 Vue3 双向绑定的核心差异

维度 Vue2 Vue3
响应式底层 Object.defineProperty Proxy
数据监听范围 仅能监听已声明的属性,新增属性需 $set 自动监听所有属性(包括新增、删除)
数组监听 重写 7 个原型方法(如 push Proxy 直接拦截数组操作
依赖收集方式 Dep 与 Watcher 关联 track/trigger 与 effect 关联
编译优化 无特殊标记,全量 diff 生成 PatchFlags,精准更新视图
自定义组件 v-model 依赖 value 属性和 input 事件 依赖 modelValue 和 update:modelValue 事件(更灵活)

总结

无论是 Vue2 还是 Vue3,双向数据绑定的核心逻辑都是 “双向触发 + 闭环更新”:

  • Model → View:通过响应式系统监听数据变化,触发视图重新渲染。
  • View → Model:通过事件监听捕获用户操作,同步修改数据,再由响应式系统触发视图二次确认。

差异主要在于响应式底层实现(Object.defineProperty vs Proxy)和编译时优化,Vue3 凭借 Proxy 的全面拦截能力和 PatchFlags 的精准更新,解决了 Vue2 中 “新增属性无响应”“数组索引监听失效” 等问题,同时提升了更新性能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐