我们来看下 计算属性源码是怎么实现的呢?
参数 getterOrOptions 是判断是写的 第一种是 函数只有getter computed(()=>{ }); 第二种 是 对象 有get 跟set computed({ get: function () {}, set: function (newValue) {console.log()} });
function computed(getterOrOptions, debugOptions, isSSR = false) {
let getter;
let setter;
// 这里则是做的判断
const onlyGetter = isFunction(getterOrOptions);
if (onlyGetter) {
getter = getterOrOptions;
// 不允许设置 setter
setter = () => {
console.warn('Write operation failed: computed value is readonly');
}
;
}
else {
// 如果是对象的写法 就去拿到它的get 跟 set
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
// 底层是通过一个 ComputedRefImpl 类来实现的
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
if (debugOptions && !isSSR) {
cRef.effect.onTrack = debugOptions.onTrack; // onTrack 跟 onTrigger 使用户可以穿进来做debug 测试用的 一般不会用到
cRef.effect.onTrigger = debugOptions.onTrigger;
}
return cRef;
}
ComputedRefImpl 的实现
class ComputedRefImpl {
constructor(getter, _setter, isReadonly, isSSR) {
this._setter = _setter;
this.dep = undefined;
this.__v_isRef = true;
// 这个是 判断值是不是 脏值 模式是脏值 用来判断需不需要更新
this._dirty = true;
// 计算属性也是通过 ReactiveEffect 来实现监听的
// 只不过 穿了一个调度器
this.effect = new ReactiveEffect(getter, () => {
// 判断值 是否是脏值 然后做更新操作
if (!this._dirty) {
this._dirty = true;
triggerRefValue(this);
}
});
this.effect.computed = this;
this.effect.active = this._cacheable = !isSSR;
}
// 这个的 get 是为了属性拿值的时候做监听 所以
const aaa = computed(()=>{
return state.name;
});
// 这个是拿值的操作 就会被监听到
// aaa.value
// 同样的 set 也是 被设置的时候 也会监听到, 但是计算属性用到 set的情况都很少
// aaa.value = 111;
get value() {
const self = toRaw(this);
// 这个是通过 get 值的时候去 做收集
trackRefValue(self);
if (self._dirty || !self._cacheable) {
self._dirty = false;
self._value = self.effect.run();
}
return self._value;
}
set value(newValue) {
this._setter(newValue);
}
}
triggerRefValue 的实现
function triggerRefValue(ref, newVal) {
ref = toRaw(ref);
if (ref.dep) {
{
triggerEffects(ref.dep, {
target: ref,
type: "set" /* TriggerOpTypes.SET */,
key: 'value',
newValue: newVal
});
}
}
}
function triggerEffects(dep, debuggerEventExtraInfo) {
// spread into array for stabilization
const effects = isArray(dep) ? dep : [...dep];
// 判断是不是计算属性
for (const effect of effects) {
if (effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo);
}
}
for (const effect of effects) {
if (!effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo);
}
}
}
function triggerEffect(effect, debuggerEventExtraInfo) {
if (effect !== activeEffect || effect.allowRecurse) {
// 这里是 判断如果有 自己的调度函数 就执行调度函数 没有就自己执行run函数
if (effect.scheduler) {
effect.scheduler();
}
else {
effect.run();
}
}
}
trackRefValue的实现
function trackRefValue(ref) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref);
{
trackEffects(ref.dep || (ref.dep = createDep()), {
target: ref,
type: "get" /* TrackOpTypes.GET */,
key: 'value'
});
}
}
}
function trackEffects(dep, debuggerEventExtraInfo) {
let shouldTrack = false;
// 这里做了一些性能的优化 先去找 有没有 有的话就不做收集了
if (effectTrackDepth <= maxMarkerBits) {
if (!newTracked(dep)) {
dep.n |= trackOpBit; // set newly tracked
shouldTrack = !wasTracked(dep);
}
}
else {
// Full cleanup mode.
shouldTrack = !dep.has(activeEffect);
}
// 这个是判断是不是该收集 这个effect
if (shouldTrack) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
// 这个是 你实例上有没有写这个debug方法 有了就做对应的处理
if (activeEffect.onTrack) {
activeEffect.onTrack(Object.assign({ effect: activeEffect }, debuggerEventExtraInfo));
}
}
}
计算属性的原理 简单来说 就是 通过effect 的ReactiveEffect 的getter 来做依赖收集,主要是去监听 依赖的响应值有没有变化,如果变化了就拿到变化了的effect通过track去做收集, 然后去执行调度器的这个方法, 从而通过trigger函数去更新值, trigger 就去拿到依赖的值 做每一个收集的effect的run方法 从而达到更新值
|