vue2双向绑定解析
Vue 2 的双向绑定原理基于 Object.defineProperty,核心源码在 src/core/observer
目录中。
1. 核心模块:observer
observer
模块负责将普通对象转换为响应式对象,主要包括以下文件:
index.js
:定义Observer
类,用于将对象转换为响应式。dep.js
:定义Dep
类,用于管理依赖(订阅者)。watcher.js
:定义Watcher
类,用于监听数据变化并触发更新。
2. Observer
类
Observer
类是 Vue 2 响应式系统的核心,它通过 Object.defineProperty
将对象的属性转换为 getter
和 setter
,从而实现依赖收集和派发更新。
源码位置:src/core/observer/index.js
export class Observer {
value: any;
dep: Dep;
vmCount: number;
constructor(value: any) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this); // 将 Observer 实例挂载到对象的 __ob__ 属性上
if (Array.isArray(value)) {
// 处理数组
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
// 处理对象
this.walk(value);
}
}
walk(obj: Object) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
observeArray(items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
}
}
Observer
类会递归地将对象的属性转换为响应式。- 对于数组,Vue 2 通过重写数组的变异方法(如
push
、pop
等)来实现响应式。
3. defineReactive
函数
defineReactive
是 Vue 2 实现响应式的核心函数,它通过 Object.defineProperty
定义属性的 getter
和 setter
。
源码位置:src/core/observer/index.js
export function defineReactive(
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep(); // 每个属性都有一个 Dep 实例,用于管理依赖
const getter = property && property.get;
const setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
let childOb = !shallow && observe(val); // 递归处理嵌套对象
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); // 收集依赖
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (newVal === value || (newVal !== newVal && value !== value)) {
return;
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal); // 对新值进行响应式处理
dep.notify(); // 通知依赖更新
},
});
}
getter
:在访问属性时,调用dep.depend()
收集依赖。setter
:在修改属性时,调用dep.notify()
通知依赖更新。
4. Dep
类
Dep
类是依赖管理器,用于存储和管理 Watcher
实例。
源码位置:src/core/observer/dep.js
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor() {
this.id = uid++;
this.subs = [];
}
addSub(sub: Watcher) {
this.subs.push(sub);
}
removeSub(sub: Watcher) {
remove(this.subs, sub);
}
depend() {
if (Dep.target) {
Dep.target.addDep(this);
}
}
notify() {
const subs = this.subs.slice();
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
}
}
Dep.target
:当前正在计算的Watcher
实例。subs
:存储所有订阅了该属性的Watcher
实例。notify
:通知所有订阅者更新。
5. Watcher
类
Watcher
类是 Vue 2 中用于监听数据变化的订阅者,它会在数据变化时触发回调函数。
源码位置:src/core/observer/watcher.js
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
getter: Function;
value: any;
constructor(
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm;
this.cb = cb;
this.id = ++uid; // uid for batching
this.deps = [];
this.newDeps = [];
this.depIds = new Set();
this.newDepIds = new Set();
this.getter = parsePath(expOrFn);
this.value = this.get();
}
get() {
pushTarget(this); // 将当前 Watcher 设置为 Dep.target
let value;
const vm = this.vm;
try {
value = this.getter.call(vm, vm);
} catch (e) {
// 处理错误
} finally {
popTarget(); // 恢复之前的 Watcher
this.cleanupDeps();
}
return value;
}
update() {
queueWatcher(this); // 将 Watcher 加入队列,等待批量更新
}
run() {
const value = this.get();
if (value !== this.value || isObject(value)) {
const oldValue = this.value;
this.value = value;
this.cb.call(this.vm, value, oldValue); // 执行回调
}
}
}
Watcher
实例会在初始化时调用get
方法,触发属性的getter
,从而收集依赖。- 当数据变化时,
Watcher
的update
方法会被调用,最终触发回调函数。
Vue 2 的双向绑定原理基于 Object.defineProperty
,通过以下步骤实现:
- 响应式化:
Observer
类将对象的属性转换为getter
和setter
。 - 依赖收集:在
getter
中调用dep.depend()
,将当前Watcher
添加到依赖列表中。 - 派发更新:在
setter
中调用dep.notify()
,通知所有依赖的Watcher
更新。 - 批量更新:
Watcher
的更新会被加入队列,异步执行,以提高性能。