Vue双向绑定是响应式系统、模板编译与依赖追踪协同实现的:通过Object.defineProperty(Vue 2)或Proxy(Vue 3)劫持数据,模板编译生成render函数产出VNode,Watcher在渲染时收集依赖,数据变更时异步通知更新。
Vue.js 的双向绑定不是魔法,而是响应式系统 + 模板编译 + 依赖追踪三者协同的结果。核心在于:数据变化能自动触发视图更新,用户输入又能反向更新数据,整个过程无需手动调用渲染函数。
响应式数据:Object.defineProperty 与 Proxy 的演进
Vue 2 使用
Object.defineProperty
劫持 data 对象的每个属性,为 getter/setter 注入依赖收集和派发更新逻辑。读取时(getter)将当前 Watcher 记为依赖;修改时(setter)通知所有依赖更新。
Vue 3 则基于
Proxy
重写响应式系统,可代理整个对象、支持数组索引/新增属性/删除属性等操作,不再需要 Vue.set 或 this.$set 的补丁方案。
注意:只有在 new Vue 或 setup() 中声明的初始属性才被代理,后期动态添加的普通属性不会触发更新(Vue 2 尤其明显)。
立即学习
“
前端免费学习笔记(深入)
”;
模板编译:从 HTML 字符串到可执行的 render 函数
Vue 在初始化时会把 template 编译为
render 函数
(开发环境可显式配置 compiler-sfc)。例如:
最终生成类似
的 VNode 描述。
这个过程包含三步:
解析 template 得到 AST 抽象语法树
静态节点标记与优化(提升 diff 效率)
生成可执行的 JavaScript render 函数
render 函数执行后返回虚拟 DOM(VNode),是真实 DOM 的轻量映射,也是后续 patch 的基础。
Vue-router参考手册
Vue-router参考手册下载
下载
依赖收集与更新派发:Watcher、Dep 与响应式链条
每个组件实例对应一个
渲染 Watcher
,它在首次执行 render 函数时“碰”到响应式属性,触发其 getter,从而将自身记录进该属性对应的 Dep(依赖管理器)中。
当数据变化调用 setter 时,Dep 会遍历通知所有收集到的 Watcher,Watcher 再异步触发组件重新 render 和 patch。
关键点:
依赖关系是「属性 → Dep → Watcher」的树状结构,非全局广播
更新是异步的(nextTick),批量合并同一轮中的多次变更,避免重复渲染
计算属性(computed)和侦听器(watch)也基于相同机制,只是 Watcher 类型不同
v-model 的本质:语法糖背后的双向同步逻辑
v-model 并非独立功能,而是 :value + @input(或特定事件)的组合封装。
例如:
等价于
要求子组件内部触发
(Vue 2)或使用 model 选项 / v-model 参数(Vue 3)
它之所以“双向”,是因为视图层输入触发事件更新数据,而数据更新又通过响应式驱动视图重绘——中间靠的是模板绑定 + 响应式系统 + 更新队列闭环。
理解这三层协作,就能明白为什么响应式数据必须初始化声明、为什么 v-for 中 key 不可省略、为什么直接赋值数组下标不会触发更新(Vue 2),以及为什么 computed 有缓存而 methods 每次执行——底层全是同一套机制在支撑。
v-model="msg"_c('input', { on: { input: $event => msg = $event.target.value } }, [_v(msg)])this.$emit('input', newValue)