实现 computed 计算属性

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
//reactivity/__test__/computed.spes
import { computed } from '../computed'
import { reactive } from '../reactive'
describe('computed', () => {
it('happy path', () => {
const user = reactive({ age: 1 })
const age = computed(() => user.age)
expect(age.value).toBe(1)
})
it('should compute lazily', () => {
const value = reactive({ foo: 1 })
const getter = jest.fn(() => value.foo)
const cValue = computed(getter)
/**不调用计算属性的时候不会执行 */
expect(getter).not.toHaveBeenCalled()
/**调用计算属性执行一次 */
expect(cValue.value).toBe(1)
expect(getter).toHaveBeenCalledTimes(1)
/**再次读取计算属性值执行一次 */
cValue.value
expect(getter).toHaveBeenCalledTimes(1)

value.foo = 2
expect(getter).toHaveBeenCalledTimes(1)

expect(cValue.value).toBe(2)
expect(getter).toHaveBeenCalledTimes(2)

cValue.value
expect(getter).toHaveBeenCalledTimes(2)
})
})

实现单测

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
//reactivity/computed
import { ReactiveEffect } from './effect'
class ComputedRefImpl {
private _getter: any
private _dirty: boolean = true
private _value: any
private _effect: any
constructor(getter) {
this._getter = getter
this._effect = new ReactiveEffect(
getter,
/* 响应式对象更新
* _dirty私有变量标记转true 表示需要重新获取新的计算结果
*/
() => {
if (!this._dirty) this._dirty = true
}
)
}
get value() {
/**缓存操作
* 通过_dirty私有变量标记判断是否被调用过
* 调用过 直接返回缓存的上次调用结果
* 没调用过 _dirty转false 保留调用结果
*/
if (this._dirty) {
this._dirty = false
this._value = this._effect.run()
}
return this._value
}
}
export const computed = getter => {
return new ComputedRefImpl(getter)
}

到这里 vue3 的核心响应式功能基本结束了