Computed
# 目录
# computed
官网释义
Takes a getter function and returns an immutable (不可改变的,不可写的) reactive ref object for the returned value from the getter.Alternatively, it can take an object with get and set functions to create a writable ref object. 使用 getter 函数,并为从 getter 返回的值返回一个不变的响应式 ref 对象。或者,它可以使用具有 get 和 set 函数的对象来创建可写的 ref 对象。
// 函数重载
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 解析 getter 和 setter
if (isFunction(getterOrOptions)) {
// 只读
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
// 可读可写
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}
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
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
computed 主要由 ComputedRefImpl 实现功能,可以传入 getter 和 setter,用于创建 ref 对象。
# ComputedRefImpl
创建 computedRef 对象。
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true // 初始就调度一次
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true;
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
// 创建 lazy effect
this.effect = effect(getter, {
// 跳过初始化的执行
lazy: true, // 自定义调度器,在执行 effect 时由交给自定义的调度器处理(调度)
// _dirty 表示是否调度过
// 注意:这里并不是说 effect 只会执行一次,而是发起了一次调度
scheduler: () => {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
// 如果当前对象被 proxy,需要将它解包装
const self = toRaw(this)
// 如果已经被调度,就执行 effect,并且响应执行后的结果,然后关闭调度
if (self._dirty) {
self._value = this.effect()
self._dirty = false
}
// 追踪依赖
track(self, TrackOpTypes.GET, 'value')
return self._value
}
set value(newValue: T) {
// 直接调用 _setter
this._setter(newValue)
}
}
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
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
与 ref 不同的有以下几点:
- 创建是 effect 是 lazy effect,也就是在初始化时并不会执行一次。
执行如下的测试代码,看一下 computed 的过程:
it('should return updated value', () => {
const value = reactive<{ foo?: number }>({})
console.log('==> 1')
const cValue = computed(() => value.foo)
let temp
console.log('==> 2')
effect(() => (temp = cValue.value))
console.log('==> 3')
expect(cValue.value).toBe(undefined)
console.log('==> 4')
value.foo = 1
console.log('==> 5')
expect(cValue.value).toBe(1)
console.log('==> 6')
expect(temp).toBe(1)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建 reactive 对象,响应式目标被代理
==> createReactiveObject { target: {} }
==> 1
// 实例化 ComputedRefImpl 对象,在构造器中生成 effect。调用 effect()。
==> createReactiveEffect [Arguments] {
'0': [Function],
'1': { lazy: true, scheduler: [Function: scheduler] } }
==> 2
==> createReactiveEffect [Arguments] { '0': [Function], '1': {} }
==> ComputedRefImpl get { _dirty: true }
==> get { target: {}, key: 'foo' }
==> track [Arguments] { '0': {}, '1': 'get', '2': 'foo' } { activeEffect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [],
options: { lazy: true, scheduler: [Function: scheduler] } } }
==> track [Arguments] {
'0':
ComputedRefImpl {
_setter: [Function],
_dirty: false,
__v_isRef: true,
effect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [Array],
options: [Object] },
__v_isReadonly: true,
_value: undefined },
'1': 'get',
'2': 'value' } { activeEffect:
{ [Function: reactiveEffect]
id: 1,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [],
options: {} } }
==> 3
==> ComputedRefImpl get { _dirty: false }
==> track [Arguments] {
'0':
ComputedRefImpl {
_setter: [Function],
_dirty: false,
__v_isRef: true,
effect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [Array],
options: [Object] },
__v_isReadonly: true,
_value: undefined },
'1': 'get',
'2': 'value' } { activeEffect: undefined }
==> 4
==> set { target: {}, key: 'foo' }
==> get { target: { foo: 1 }, key: '__v_raw' }
==> trigger [Arguments] { '0': { foo: 1 }, '1': 'add', '2': 'foo', '3': 1 }
==> ComputedRefImpl effect scheduler { _dirty: false }
==> trigger [Arguments] {
'0':
ComputedRefImpl {
_setter: [Function],
_dirty: true,
__v_isRef: true,
effect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [Array],
options: [Object] },
__v_isReadonly: true,
_value: undefined },
'1': 'set',
'2': 'value' }
==> ComputedRefImpl get { _dirty: true }
==> get { target: { foo: 1 }, key: 'foo' }
==> track [Arguments] { '0': { foo: 1 }, '1': 'get', '2': 'foo' } { activeEffect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [],
options: { lazy: true, scheduler: [Function: scheduler] } } }
==> track [Arguments] {
'0':
ComputedRefImpl {
_setter: [Function],
_dirty: false,
__v_isRef: true,
effect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [Array],
options: [Object] },
__v_isReadonly: true,
_value: 1 },
'1': 'get',
'2': 'value' } { activeEffect:
{ [Function: reactiveEffect]
id: 1,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [],
options: {} } }
==> 5
==> ComputedRefImpl get { _dirty: false }
==> track [Arguments] {
'0':
ComputedRefImpl {
_setter: [Function],
_dirty: false,
__v_isRef: true,
effect:
{ [Function: reactiveEffect]
id: 0,
allowRecurse: false,
_isEffect: true,
active: true,
raw: [Function],
deps: [Array],
options: [Object] },
__v_isReadonly: true,
_value: 1 },
'1': 'get',
'2': 'value' } { activeEffect: undefined }
==> 6
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# 文章小结
编辑 (opens new window)
上次更新: 2022/04/15, 00:23:56