在Vue.js框架中,数据监听是一个核心功能,它允许开发者创建响应式的应用程序。这一功能的实现主要依赖于Vue的响应式系统,其中最关键的部分是如何监控数据变化并响应这些变化。以下是一个对Vue中数据监听实现方式的简要概述,适用于理解Vue 2.x版本的源码结构。
核心概念
-
响应式对象: Vue使用
Object.defineProperty
来把这些属性转换为getter/setter,从而能够监听数据的变化。这个过程在Vue实例初始化时,通过data
选项中的数据进行处理。 -
依赖收集: 在getter中进行依赖收集。当组件或者指令访问某个数据时,会触发getter,此时,当前活动的观察者(Watcher)会被添加到这个数据属性的依赖列表中。这样,当数据变化时,可以通知所有依赖于此数据的观察者。
-
派发更新: 在setter中处理数据变化。当数据被修改时,setter会被触发,它将通知所有依赖于这个数据的观察者,告知它们所依赖的数据已经改变。
实现细节
初始化响应式系统
function initData(vm) {
let data = vm.$options.data;
data = vm._data = typeof data === 'function' ? data.call(vm, vm) : data || {};
const keys = Object.keys(data);
keys.forEach(key => {
proxy(vm, `_data`, key);
});
observe(data);
}
function proxy(vm, sourceKey, key) {
Object.defineProperty(vm, key, {
get: function proxyGetter() {
return vm[sourceKey][key];
},
set: function proxySetter(val) {
vm[sourceKey][key] = val;
}
});
}
创建观察者对象和依赖类
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
this.getter = parsePath(expOrFn);
this.value = this.get();
}
get() {
Dep.target = this;
let value = this.getter.call(this.vm, this.vm);
Dep.target = null;
return value;
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
数据劫持与依赖收集
function observe(value) {
if (typeof value !== 'object') return;
let ob;
if (typeof value.__ob__ !== 'undefined') {
ob = value.__ob__;
} else {
ob = new Observer(value);
}
return ob;
}
class Observer {
constructor(value) {
this.value = value;
this.dep = new Dep();
def(value, '__ob__', this);
if (Array.isArray(value)) {
// 处理数组
// 略
} else {
this.walk(value);
}
}
walk(obj) {
const keys = Object.keys(obj);
keys.forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
function defineReactive(obj, key, val) {
const dep = new Dep();
let childOb = observe(val);
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target);
if (childOb) {
childOb.dep.addSub(Dep.target);
}
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
childOb = observe(newVal);
dep.notify();
}
});
}
结论
Vue的响应式系统是通过利用JavaScript的Object.defineProperty
方法来劫持各个属性的getter和setter,实现数据的观察与派发更新。这种机制使得Vue能够非常高效地更新DOM,确保用户界面与数据状态保持一致。这个系统的设计也展示了现代前端框架中数据驱动视图的核心思想。