Vue 的 computed 的实现原理及代码解析
Vue 的 computed 属性的实现原理主要涉及依赖追踪、缓存机制和响应式更新。下面是一个简化的代码解析,帮助理解其实现原理。
依赖追踪和缓存机制
-
依赖追踪:
- Vue 使用
Object.defineProperty或Proxy来拦截对响应式数据的访问。 - 当访问计算属性时,Vue 会追踪其依赖的数据。
- Vue 使用
-
缓存机制:
- 计算属性的结果会被缓存,只有当依赖的数据发生变化时,才会重新计算。
-
响应式更新:
- 当依赖的数据发生变化时,Vue 会通知相关的
Watcher对象,重新计算计算属性的值。
- 当依赖的数据发生变化时,Vue 会通知相关的
简化代码解析
以下是一个简化的 Vue 计算属性实现示例:
class Dep {
constructor() {
this.subs = [];
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target);
}
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null;
class Watcher {
constructor(getter, callback) {
this.getter = getter;
this.callback = callback;
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.getter();
Dep.target = null;
return value;
}
update() {
const oldValue = this.value;
this.value = this.get();
this.callback.call(this, this.value, oldValue);
}
}
class ComputedProperty {
constructor(getter) {
this.getter = getter;
this.dep = new Dep();
this.value = null;
this.dirty = true;
this.watcher = new Watcher(this.get.bind(this), this.update.bind(this));
}
get() {
if (this.dirty) {
this.value = this.getter();
this.dirty = false;
}
if (Dep.target) {
this.dep.depend();
}
return this.value;
}
update() {
this.dirty = true;
this.dep.notify();
}
}
// 示例使用
const data = {
firstName: 'John',
lastName: 'Doe'
};
const depFirstName = new Dep();
const depLastName = new Dep();
Object.defineProperty(data, 'firstName', {
get() {
depFirstName.depend();
return 'John';
},
set(newValue) {
if (newValue !== 'John') {
depFirstName.notify();
}
}
});
Object.defineProperty(data, 'lastName', {
get() {
depLastName.depend();
return 'Doe';
},
set(newValue) {
if (newValue !== 'Doe') {
depLastName.notify();
}
}
});
const computedFullName = new ComputedProperty(() => {
depFirstName.depend();
depLastName.depend();
return `${data.firstName} ${data.lastName}`;
});
const watcherFullName = new Watcher(() => {
return computedFullName.get();
}, (newValue, oldValue) => {
console.log(`fullName changed from ${oldValue} to ${newValue}`);
});
console.log(watcherFullName.value); // John Doe
data.firstName = 'Jane'; // fullName changed from John Doe to Jane Doe
关键点解析
-
Dep 类:
depend方法用于将当前的Watcher添加到依赖列表中。notify方法用于通知所有订阅的Watcher更新。
-
Watcher 类:
get方法用于获取值,并在获取过程中收集依赖。update方法用于在依赖变化时重新计算值。
-
ComputedProperty 类:
get方法用于获取计算属性的值,并在需要时重新计算。update方法用于标记计算属性为脏状态,以便在下次访问时重新计算。
通过这些类和方法,Vue 实现了计算属性的依赖追踪、缓存和响应式更新。
Vue 的 watch的实现原理及原理实现代码解析
Vue 的 watch 选项用于观察和响应 Vue 实例上的数据变化。其实现原理主要依赖于 Vue 的响应式系统。下面详细解析其实现原理及代码实现。
实现原理
- 响应式系统:Vue 通过
Object.defineProperty或Proxy对数据进行拦截,监听数据的变化。 - 依赖收集:当组件渲染时,会访问响应式数据,Vue 会收集这些数据的依赖(即哪些地方使用了这些数据)。
- 触发更新:当数据发生变化时,Vue 会通知所有依赖该数据的地方进行更新。
- Watcher:
watch选项内部会创建一个Watcher实例,当监听的数据变化时,Watcher会执行回调函数。
代码解析
Vue 的 watch 实现主要涉及 Watcher 类和响应式系统的集成。以下是一个简化的代码解析。
1. 响应式系统
Vue 使用 Object.defineProperty 来拦截数据的访问和修改:
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
dep.depend();
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
});
}
2. Dep 类
Dep 类用于管理依赖关系:
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
depend() {
if (Dep.target) {
Dep.target.addDep(this);
}
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null;
3. Watcher 类
Watcher 类用于观察数据变化并执行回调函数:
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm;
this.getter = parsePath(expOrFn);
this.cb = cb;
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.getter.call(this.vm, this.vm);
Dep.target = null;
return value;
}
addDep(dep) {
dep.addSub(this);
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
function parsePath(path) {
const segments = path.split('.');
return function(obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]];
}
return obj;
};
}
4. 初始化 Watcher
在 Vue 实例初始化时,会根据 watch 选项创建 Watcher 实例:
function initWatch(vm, watch) {
for (const key in watch) {
const handler = watch[key];
if (Array.isArray(handler)) {
handler.forEach(handler => createWatcher(vm, key, handler));
} else {
createWatcher(vm, key, handler);
}
}
}
function createWatcher(vm, expOrFn, handler, options) {
if (typeof handler === 'string') {
handler = vm[handler];
}
return vm.$watch(expOrFn, handler, options);
}
Vue.prototype.$watch = function(expOrFn, cb, options) {
const vm = this;
options = options || {};
const watcher = new Watcher(vm, expOrFn, cb, options);
if (options.immediate) {
cb.call(vm, watcher.value);
}
return function unwatchFn() {
watcher.teardown();
};
};
总结
- 响应式系统:通过
Object.defineProperty拦截数据访问和修改。 - Dep 类:管理依赖关系,收集和通知依赖。
- Watcher 类:观察数据变化并执行回调函数。
- 初始化 Watcher:在 Vue 实例初始化时,根据
watch选项创建Watcher实例。
通过这些步骤,Vue 实现了对数据变化的监听和响应。