1. 1. computed
    1. 1.1. 调度器scheduler
    2. 1.2. 实现computed
Vue3源码学习——响应式原理(03)

computed

vue3的computed与vue2的一致,都有个特性,即当依赖更新触发时,并不会立即计算更新后的值,而是等computed触发getter需要展示时才被计算,这里就需要在effect中引入一个调度器scheduler的概念

调度器scheduler

什么是调度器呢?顾名思义,就是调度一些任务,当某些任务需要按照我们期望的顺序执行时,就需要使用到调度器。它通常是用来做一些异步或周期性的任务的。

computed中,我们需要控制的是它依赖更新的触发时机,因此,我们需要改进effecttrigger函数。

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)
// 配置 lazy ,则不会在effect创建时立刻执行
if (!options.lazy) {
effect()
}
return effect
}

let uid = 0

function createReactiveEffect(fn, options) {
// effect 创建增加 options 配置,其中就包括 scheduler
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
}

/**
* 依赖触发
* @param {*} target
* @param {*} key
*/
export const trigger = (target, key) => {
const depsMap = targetMap.get(target)
if (!depsMap) {
// never been tracked
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)
}
})
}
}
// 获取对应key的deps,它是个Set对象
add(depsMap.get(key))

const run = (effect) => {
// 增加调度器判断,执行调度器函数
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
}
// 遍历执行所有依赖相关的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) {
// _dirty标记当前的computed是否需要重新计算值,为true时表示值需要更新,false时不需要更新
this._dirty = true
this.__v_isRef = true
this._setter = setter
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
// 当scheduler执行时,表示computed所依赖计算的值更新触发
// 如果此时 this._dirty 为 false,表示computed值已经需要重新更新
// 此时需要将 this._dirty 重置为 true,并触发 computed 值相关依赖的更新
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)
}