vue3-computed的简易实现(3)
基本用法
官方用法:接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value
暴露 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
根据上面可以知道, computed函数接受两种方式的传值, 下面来简单实现一下
// shared文件里面添加一下isFunction
export function isFunction(value: unknown): value is Record<any, any> {
return typeof value === "function";
}
computed函数实现
在 package/reactivity/src 新建 computed.ts 文件
import { isFunction } from "@vue/shared";
export function computed(getterOrOptions) {
let onlyGetter = isFunction(getterOrOptions);
let getter;
let setter;
// 如果只有getter那么默认就不给setter了
if (onlyGetter) {
getter = getterOrOptions;
setter = () => {
console.warn("no set");
};
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
// 再降参数传给ComputedRefImpl类
return new ComputedRefImpl(getter, setter);
}
ComputedRefImpl类实现
import { ReactiveEffect } from "./effect";
class ComputedRefImpl {
public effect;
public _dirty = true; // 用来检测值是否需要更新
public _value;
public dep = new Set(); // 记录自身值依赖
constructor(public getter, public setter) {
// 将getter函数放到ReactiveEffect中, 里面的依赖就会被当前的effect收集起来
this.effect = new ReactiveEffect(getter, () => {});
}
// 这里利用的是class中的getter和setter
get value() {
// 如果值有变动才需要进行取值, 不然就直接返回原值
if (this._dirty) {
this._dirty = false;
this._value = this.effect.run();
}
return this._value;
}
set value(newValue) {
this.setter(newValue);
}
}
下面就是考虑在依赖变动的时候如何触发effect, 流向是 值变动 -> computedEffect -> 外层effect, 因为自身只有value属性的原因, 我们只需要将effect收集到我们的dep属性中,将来直接触发就可以了,我们改造一下 track 和 trigger 函数
const targetMap = new WeakMap();
export function track(target, type, key) {
if (activeEffect) {
// 看看是否已经存在依赖
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
trackEffects(dep); // 调用函数
}
}
export function trackEffects(dep) {
if (activeEffect) {
let shouldTrack = !dep.has(activeEffect);
if (shouldTrack) {
dep.add(activeEffect);
activeEffect.deps.push(dep); // 保存dep, 方便后续处理
}
}
}
export function trigger(target, type, key?, newValue?, oldValue?) {
const depsMap = targetMap.get(target); // 获取对应的映射表
if (!depsMap) {
return;
}
let effects = depsMap.get(key);
if (effects) {
triggerEffects(effects); // 调用函数
}
}
export function triggerEffects(effects) {
effects = new Set(effects);
effects.forEach((effect) => {
if (effect !== activeEffect) {
if (effect.scheduler) {
effect.scheduler();
} else {
effect.run();
}
}
});
}
在获取value值的时候就先进行依赖收集, 一旦 new ReactiveEffect 的 scheduler 函数执行, 证明computed函数中的值发生了变动, 那么就应该要重新取值, 并且更新外层对 computed属性的 effect
import { ReactiveEffect, trackEffects, triggerEffects } from "./effect";
class ComputedRefImpl {
constructor(public getter, public setter) {
// 将getter函数放到ReactiveEffect中, 里面的依赖就会被当前的effect收集起来
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true; // 证明值重新设置过, 下次需要获取新值
triggerEffects(this.dep);
}
});
}
// 这里利用的是class中的getter和setter
get value() {
// 进行依赖收集
trackEffects(this.dep);
if (this._dirty) {
this._dirty = false;
this._value = this.effect.run();
}
return this._value;
}
}
总体的执行流程
转载自:https://juejin.cn/post/7139814188976701471