Vue 双向绑定原理详解
Vue 的双向绑定是其最核心的特性之一,它实现了数据(model)和视图(view)的自动同步。下面我将详细解释其工作原理。
1. 什么是双向绑定
双向绑定指的是:
- 当数据发生变化时,视图自动更新
- 当视图发生变化时(如表单输入),数据自动更新
2. 核心实现机制
Vue 的双向绑定主要通过以下几个部分实现:
2.1 数据劫持(Observer)
Vue 使用 Object.defineProperty() (Vue 2.x) 或 Proxy (Vue 3.x) 来劫持数据的 getter 和 setter。
// Vue 2.x 使用 Object.defineProperty
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`get ${key}:${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`set ${key}:${newVal}`);
val = newVal;
// 触发更新
}
}
});
}
2.2 依赖收集(Dep)
每个被观察的数据属性都有一个 Dep 实例,用于收集所有依赖该数据的 Watcher。
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
2.3 观察者(Watcher)
Watcher 是观察者,当数据变化时,它会收到通知并执行更新操作。
let target = null;
class Watcher {
constructor(func) {
target = func;
func();
target = null;
}
}
2.4 编译器(Compiler)
Vue 的编译器会解析模板,识别出其中的指令和插值表达式,并建立与数据的绑定关系。
3. 完整工作流程
-
初始化阶段:
- Vue 实例化时,对 data 进行递归遍历,使用
Object.defineProperty设置 getter/setter - 编译模板,解析指令和插值表达式,创建 Watcher
- Vue 实例化时,对 data 进行递归遍历,使用
-
依赖收集:
- 当渲染函数执行时,会读取数据,触发 getter
- getter 中将当前的 Watcher 添加到 Dep 中
-
数据更新:
- 当数据被修改时,触发 setter
- setter 中通知 Dep,Dep 通知所有 Watcher
- Watcher 触发重新渲染
-
视图更新:
- 当表单元素(v-model)输入时,触发 input 事件
- 事件回调中更新对应的数据
4. v-model 的实现
v-model 是语法糖,它实际上结合了:
:value绑定数据到视图@input监听视图变化并更新数据
例如:
<input v-model="message">
等价于:
<input
:value="message"
@input="message = $event.target.value"
>
5. Vue 2.x 与 Vue 3.x 的区别
| 特性 | Vue 2.x | Vue 3.x |
|---|---|---|
| 数据劫持方式 | Object.defineProperty | Proxy |
| 数组监听 | 需要特殊处理 | 原生支持 |
| 性能 | 递归遍历所有属性 | 惰性劫持 |
Vue 3 使用 Proxy 的优势:
- 可以直接监听对象而非属性
- 可以直接监听数组的变化
- 有更多的拦截方法(13种)
- 性能更好
6. 简单实现示例
// 简易响应式系统
function observe(data) {
if (!data || typeof data !== 'object') return;
Object.keys(data).forEach(key => {
let val = data[key];
const dep = new Dep();
observe(val); // 递归子属性
Object.defineProperty(data, key, {
get() {
if (Dep.target) dep.depend();
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
observe(newVal); // 新值是对象时继续监听
dep.notify();
}
});
});
}
// 使用
const data = { message: 'Hello' };
observe(data);
new Watcher(() => {
console.log('数据变化:', data.message);
});
data.message = 'World'; // 触发更新
这就是 Vue 双向绑定的核心原理,通过数据劫持结合发布-订阅模式,实现了数据与视图的自动同步。