持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
这篇主要是对计算属性computed的实现,主要是在effect的基础上实现的。 api用法:v3.cn.vuejs.org/api/compute…
// 用法1 传函数 只能get
let f = computed(()=>{
return state.a+state.b
})
// 2 接受一个具有 `get` 和 `set` 函数的对象,用来创建可写的 ref 对象
let f = computed({
get(){
return state.a + state.b
},
set(value){
state.a = value
}
})
f.value = 'a'
// 例子
effect(()=>{
app.innerHTML = f.value
})
特性:
响应式更新(effect中有用到计算属性的,当计算属性里的依赖属性变化时会重新执行effect);
结果缓存;
惰性求值(真正用到的时候才去计算)
流程
判断传入参数是对象还是函数,得到相应的
get和set,返回构造类ComputedRefImpl构造函数中 构造一个
effect实例,类中定义一个变量
dirty,默认为true,当需要获取这个computed值时,走的是get,检测dirty,为真时去执行computer里的函数,得到值赋值给value进行缓存、将dirty设为false并返回value,下次拿的时候由于dirty为false,直接拿缓存即可。在
get中同响应式一样,设置变量dep存储effect依赖数组,如例子的()=>{ app.innerHTML = f.value }在
effect实例的scheduler回调中,可以监听依赖属性变动,触发回调时,dirty为false则要改成true,去执行effect依赖数组。dirty为true才能让computed值获取时拿到最新的。
源码
import {
isTracking,
ReactiveEffect,
trackEffects,
triggerEffects,
} from "./effect";
function isFunction(val) {
return typeof val == "function";
}
export function computed(getterOrOptions) {
// 两种用法,一种是传入函数,一种是传入对象
const onlyGetter = isFunction(getterOrOptions);
let getter;
let setter;
if (onlyGetter) {
getter = getterOrOptions;
// 不给赋值
setter = () => {};
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
return new ComputedRefImpl(getter, setter);
}
class ComputedRefImpl {
public dep; // this.dep = undefined;
// 脏数据 初始化或者属性变动的时候为true,用来确保缓存的值是最新的
public _dirty = true; // this._dirty = true;
public effect; // 计算属性是依赖于effect的
public _value;
constructor(getter, public setter) {
// 只有调用computed()才执行一次
// 这里将计算属性包成一个effect,当里面的属性变动时会触发回调
this.effect = new ReactiveEffect(getter, () => {
// 稍后计算属性依赖的值变化 改为调用此函数
if (!this._dirty) {
this._dirty = true;
// 执行依赖数组们
triggerEffects(this.dep);
}
});
}
get value() {
// 取值时会走get方法
if (isTracking()) {
// 收集effect依赖数组
trackEffects(this.dep || (this.dep = new Set()));
}
if (this._dirty) {
// 将结果缓存到this._value 这样就不用每次都run了
this._value = this.effect.run();
this._dirty = false;
}
return this._value;
}
set value(newValue) {
this.setter(newValue); // 如果修改计算属性的值 就触发你自己的set方法
}
}