聊聊 Vue2 和 Vue3 的双向数据绑定,Model 如何改变 View,View 又是如何改变 Model 的
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 中 “新增属性无响应”“数组索引监听失效” 等问题,同时提升了更新性能。
更多推荐
所有评论(0)