computed
vue3的computed
与vue2的一致,都有个特性,即当依赖更新触发时,并不会立即计算更新后的值,而是等computed
触发getter
需要展示时才被计算,这里就需要在effect
中引入一个调度器scheduler
的概念
调度器scheduler
什么是调度器呢?顾名思义,就是调度一些任务,当某些任务需要按照我们期望的顺序执行时,就需要使用到调度器。它通常是用来做一些异步或周期性的任务的。
在computed
中,我们需要控制的是它依赖更新的触发时机,因此,我们需要改进effect
与trigger
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| export const effect = (fn, options = {}) => { if (isEffect(fn)) { fn = fn.raw } const effect = createReactiveEffect(fn, options) if (!options.lazy) { effect() } return effect }
let uid = 0
function createReactiveEffect(fn, options) { const effect = function reactiveEffect() { if (!effectStack.includes(effect)) { try { effectStack.push(effect) activeEffect = effect return fn() } finally { effectStack.pop() activeEffect = effectStack[effectStack.length - 1] } } } effect.id = uid++ effect._isEffect = true effect.raw = fn effect.options = options return effect }
export const trigger = (target, key) => { const depsMap = targetMap.get(target) if (!depsMap) { return } console.log(`触发 Setter 依赖触发 trigger -> target:`, target, ` key:${key}`) const effects = new Set() const add = (effectsToAdd) => { if (effectsToAdd) { effectsToAdd.forEach((effect) => { if (effect !== activeEffect) { effects.add(effect) } }) } } add(depsMap.get(key))
const run = (effect) => { if (effect.options.scheduler) { effect.options.scheduler(effect) } else { effect() } } effects.forEach(run) }
|
实现computed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class ComputedRefImpl { constructor(getter, setter, isReadonly) { this._dirty = true this.__v_isRef = true this._setter = setter this.effect = effect(getter, { lazy: true, scheduler: () => { if (!this._dirty) { this._dirty = true trigger(this, 'value') } } }) this.__v_isReadonly = isReadonly }
get value() { if (this._dirty) { this._value = this.effect() this._dirty = false } track(this, 'value') return this._value }
set value(newValue) { this._setter(newValue) } }
export const computed = (getterOrOptions) => { let getter let setter
if (isFunction(getterOrOptions)) { getter = getterOrOptions setter = NOOP } else { getter = getterOrOptions.get setter = getterOrOptions.set } return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set) }
|