在 Vue 中,数据双向绑定是通过 数据劫持 + 发布者-订阅者模式 实现的。
核心原理
数据劫持(响应式基础)
Vue 2.x:使用
Object.defineProperty()劫持对象属性,为每个属性添加getter和setter。getter:收集依赖(记录哪些组件依赖了该数据)。setter:数据变化时触发依赖更新(通知相关组件重新渲染)。
Vue 3.x:改用
Proxy代理整个对象,直接监听属性操作(包括数组索引变化、新增属性等),性能更优且无 Vue 2 的限制。
依赖收集与更新(发布者-订阅者模式)
依赖收集:组件渲染时,访问数据触发
getter,将当前组件的Watcher(订阅者)存入Dep(依赖收集器)。触发更新:数据修改触发
setter,Dep通知所有关联的Watcher,Watcher调用组件更新函数(如patch)重新渲染视图。
双向绑定(v-model)
本质是 数据绑定(单向) + 事件监听 的语法糖:
<input v-model="message" /><!-- 等价于 --><input :value="message" @input="message = $event.target.value" />视图变化(如用户输入)通过事件更新数据,数据变化自动同步到视图。
关键角色
Observer:递归遍历数据,用
Object.defineProperty或Proxy劫持属性,使其响应式。Dep:依赖收集器,每个响应式属性都有一个
Dep实例,管理该属性的Watcher列表。Watcher:订阅者(如组件渲染函数、计算属性),数据变化时收到通知并执行更新。
Compile:解析模板指令(如
v-model),初始化视图绑定和事件监听。
流程简述
初始化:
Observer劫持数据,将其转为响应式。Compile编译模板,将v-model转为:value+@input,并创建Watcher。
数据 → 视图:
数据变化 →
setter触发 →Dep通知Watcher→Watcher更新视图。
视图 → 数据:
用户输入 → 触发
@input事件 → 更新数据 → 自动触发视图更新。
Vue 2 vs Vue 3
面试回答示例
“Vue 的双向绑定基于数据劫持和发布者-订阅者模式。
数据劫持:Vue 2 用Object.defineProperty为属性添加getter/setter,Vue 3 用Proxy代理整个对象。
依赖管理:getter收集依赖(Watcher),setter通知更新。
双向绑定:v-model是语法糖,通过数据绑定(value)和事件监听(@input)实现视图与数据的双向同步。
关键流程:数据变化触发setter→ 通知Watcher→ 更新视图;用户输入触发事件 → 更新数据 → 自动同步视图。”
常见追问点
为什么 Vue 2 无法监听数组索引变化?
Object.defineProperty无法直接监听数组索引,需重写数组方法(如push、splice)。
Proxy的优势?直接监听对象,无需递归,支持动态属性和数组操作,性能更好。
Dep和Watcher的关系?一个响应式属性对应一个
Dep,Dep管理多个Watcher(如多个组件依赖同一数据)。
掌握以上要点,可清晰阐述 Vue 双向绑定的核心机制。