Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2054671

Browse files
author
febobo
committed
add ref and computed
1 parent f345e30 commit 2054671

File tree

5 files changed

+682
-0
lines changed

5 files changed

+682
-0
lines changed

‎docs/global/index.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!--
2+
* @Author: your name
3+
* @Date: 2020年07月24日 18:20:29
4+
* @LastEditTime: 2020年07月24日 18:20:30
5+
* @LastEditors: Please set LastEditors
6+
* @Description: In User Settings Edit
7+
* @FilePath: /work/vue3-doc/docs/global/index.md
8+
-->

‎docs/reactivity/computed.md‎

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
### computed
2+
3+
> 传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。
4+
5+
```js
6+
const count = ref(1)
7+
const plusOne = computed(() => count.value + 1)
8+
9+
console.log(plusOne.value) // 2
10+
11+
plusOne.value++ // 错误!
12+
```
13+
14+
> 或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
15+
```js
16+
const count = ref(1)
17+
const plusOne = computed({
18+
get: () => count.value + 1,
19+
set: (val) => {
20+
count.value = val - 1
21+
},
22+
})
23+
24+
plusOne.value = 1
25+
console.log(count.value) // 0
26+
```
27+
28+
更多文档: [https://vue3js.cn/vue-composition-api/#computed](https://vue3js.cn/vue-composition-api/#computed)
29+
30+
### 正文
31+
32+
计算属性,可能会依赖其他 `reactive` 的值,同时会延迟和缓存计算值
33+
34+
```js
35+
export function computed<T>(
36+
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
37+
) {
38+
let getter: ComputedGetter<T>
39+
let setter: ComputedSetter<T>
40+
41+
// 如果传入是 function 说明是只读 computed
42+
if (isFunction(getterOrOptions)) {
43+
getter = getterOrOptions
44+
setter = __DEV__
45+
? () => {
46+
console.warn('Write operation failed: computed value is readonly')
47+
}
48+
: NOOP
49+
} else {
50+
// 不是方法说明是自定义的 getter setter
51+
getter = getterOrOptions.get
52+
setter = getterOrOptions.set
53+
}
54+
55+
let dirty = true
56+
let value: T
57+
let computed: ComputedRef<T>
58+
59+
// 创建 effect, 我们在看 effect 源码时知道了传入 lazy 代表不会立即执行,computed 表明 computed 上游依赖改变的时候,会优先 trigger runner effect, scheduler 表示 effect trigger 的时候会调用 scheduler 而不是直接调用 effect
60+
const runner = effect(getter, {
61+
lazy: true,
62+
// mark effect as computed so that it gets priority during trigger
63+
computed: true,
64+
scheduler: () => {
65+
// 在触发更新时把dirty置为true, 不会立即更新
66+
if (!dirty) {
67+
dirty = true
68+
trigger(computed, TriggerOpTypes.SET, 'value')
69+
}
70+
}
71+
})
72+
73+
// 构造一个 computed 返回
74+
computed = {
75+
__v_isRef: true,
76+
// expose effect so computed can be stopped
77+
effect: runner,
78+
get value() {
79+
// dirty为ture, get操作时,执行effect获取最新值
80+
//
81+
if (dirty) {
82+
value = runner()
83+
dirty = false
84+
}
85+
// dirty为false, 表示值未更新,直接返回
86+
track(computed, TrackOpTypes.GET, 'value')
87+
return value
88+
},
89+
set value(newValue: T) {
90+
setter(newValue)
91+
}
92+
} as any
93+
return computed
94+
}
95+
```

‎docs/reactivity/computed.spec.md‎

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
### computed.spec
2+
3+
> 传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。
4+
5+
```js
6+
const count = ref(1)
7+
const plusOne = computed(() => count.value + 1)
8+
9+
console.log(plusOne.value) // 2
10+
11+
plusOne.value++ // 错误!
12+
```
13+
14+
> 或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
15+
```js
16+
const count = ref(1)
17+
const plusOne = computed({
18+
get: () => count.value + 1,
19+
set: (val) => {
20+
count.value = val - 1
21+
},
22+
})
23+
24+
plusOne.value = 1
25+
console.log(count.value) // 0
26+
```
27+
28+
更多文档: [https://vue3js.cn/vue-composition-api/#computed](https://vue3js.cn/vue-composition-api/#computed)
29+
30+
### 正文
31+
32+
1. 每次返回的是最新的值
33+
```js
34+
it('should return updated value', () => {
35+
const value = reactive<{ foo?: number }>({})
36+
const cValue = computed(() => value.foo)
37+
expect(cValue.value).toBe(undefined)
38+
value.foo = 1
39+
expect(cValue.value).toBe(1)
40+
})
41+
```
42+
43+
2. 计算属性默认是 `lazy` 不会立即执行, 取的值未发生变化不会执行
44+
```js
45+
it('should compute lazily', () => {
46+
const value = reactive<{ foo?: number }>({})
47+
const getter = jest.fn(() => value.foo)
48+
const cValue = computed(getter)
49+
50+
// lazy
51+
expect(getter).not.toHaveBeenCalled()
52+
53+
expect(cValue.value).toBe(undefined)
54+
expect(getter).toHaveBeenCalledTimes(1)
55+
56+
// should not compute again
57+
cValue.value
58+
expect(getter).toHaveBeenCalledTimes(1)
59+
60+
// should not compute until needed
61+
value.foo = 1
62+
expect(getter).toHaveBeenCalledTimes(1)
63+
64+
// now it should compute
65+
expect(cValue.value).toBe(1)
66+
expect(getter).toHaveBeenCalledTimes(2)
67+
68+
// should not compute again
69+
cValue.value
70+
expect(getter).toHaveBeenCalledTimes(2)
71+
})
72+
```
73+
74+
3. 如果有`effect`是依赖 `computed` 结果的,当它改变时,`effect` 也会执行
75+
```js
76+
it('should trigger effect', () => {
77+
const value = reactive<{ foo?: number }>({})
78+
const cValue = computed(() => value.foo)
79+
let dummy
80+
effect(() => {
81+
dummy = cValue.value
82+
})
83+
expect(dummy).toBe(undefined)
84+
value.foo = 1
85+
expect(dummy).toBe(1)
86+
})
87+
```
88+
89+
4. `computed` 之间可以相互依赖
90+
```js
91+
it('should work when chained', () => {
92+
const value = reactive({ foo: 0 })
93+
const c1 = computed(() => value.foo)
94+
const c2 = computed(() => c1.value + 1)
95+
expect(c2.value).toBe(1)
96+
expect(c1.value).toBe(0)
97+
value.foo++
98+
expect(c2.value).toBe(2)
99+
expect(c1.value).toBe(1)
100+
})
101+
```
102+
103+
5. 参照3,4条
104+
```js
105+
it('should trigger effect when chained', () => {
106+
const value = reactive({ foo: 0 })
107+
const getter1 = jest.fn(() => value.foo)
108+
const getter2 = jest.fn(() => {
109+
return c1.value + 1
110+
})
111+
const c1 = computed(getter1)
112+
const c2 = computed(getter2)
113+
114+
let dummy
115+
effect(() => {
116+
dummy = c2.value
117+
})
118+
expect(dummy).toBe(1)
119+
expect(getter1).toHaveBeenCalledTimes(1)
120+
expect(getter2).toHaveBeenCalledTimes(1)
121+
value.foo++
122+
expect(dummy).toBe(2)
123+
// should not result in duplicate calls
124+
expect(getter1).toHaveBeenCalledTimes(2)
125+
expect(getter2).toHaveBeenCalledTimes(2)
126+
})
127+
128+
it('should trigger effect when chained (mixed invocations)', () => {
129+
const value = reactive({ foo: 0 })
130+
const getter1 = jest.fn(() => value.foo)
131+
const getter2 = jest.fn(() => {
132+
return c1.value + 1
133+
})
134+
const c1 = computed(getter1)
135+
const c2 = computed(getter2)
136+
137+
let dummy
138+
effect(() => {
139+
dummy = c1.value + c2.value
140+
})
141+
expect(dummy).toBe(1)
142+
143+
expect(getter1).toHaveBeenCalledTimes(1)
144+
expect(getter2).toHaveBeenCalledTimes(1)
145+
value.foo++
146+
expect(dummy).toBe(3)
147+
// should not result in duplicate calls
148+
expect(getter1).toHaveBeenCalledTimes(2)
149+
expect(getter2).toHaveBeenCalledTimes(2)
150+
})
151+
```
152+
153+
6. `computed` 可以 `stop`, `stop` 后不再响应
154+
```js
155+
it('should no longer update when stopped', () => {
156+
const value = reactive<{ foo?: number }>({})
157+
const cValue = computed(() => value.foo)
158+
let dummy
159+
effect(() => {
160+
dummy = cValue.value
161+
})
162+
expect(dummy).toBe(undefined)
163+
value.foo = 1
164+
expect(dummy).toBe(1)
165+
stop(cValue.effect)
166+
value.foo = 2
167+
expect(dummy).toBe(1)
168+
})
169+
```
170+
171+
7. 支持自定义 `setter` , `setter` 会触发 `effect`
172+
```js
173+
it('should support setter', () => {
174+
const n = ref(1)
175+
const plusOne = computed({
176+
get: () => n.value + 1,
177+
set: val => {
178+
n.value = val - 1
179+
}
180+
})
181+
182+
expect(plusOne.value).toBe(2)
183+
n.value++
184+
expect(plusOne.value).toBe(3)
185+
186+
plusOne.value = 0
187+
expect(n.value).toBe(-1)
188+
})
189+
190+
it('should trigger effect w/ setter', () => {
191+
const n = ref(1)
192+
const plusOne = computed({
193+
get: () => n.value + 1,
194+
set: val => {
195+
n.value = val - 1
196+
}
197+
})
198+
199+
let dummy
200+
effect(() => {
201+
dummy = n.value
202+
})
203+
expect(dummy).toBe(1)
204+
205+
plusOne.value = 0
206+
expect(dummy).toBe(-1)
207+
})
208+
```
209+
210+
8. 默认是只读对象,修改会抛出错误
211+
```js
212+
it('should warn if trying to set a readonly computed', () => {
213+
const n = ref(1)
214+
const plusOne = computed(() => n.value + 1)
215+
;(plusOne as WritableComputedRef<number>).value++ // Type cast to prevent TS from preventing the error
216+
217+
expect(
218+
'Write operation failed: computed value is readonly'
219+
).toHaveBeenWarnedLast()
220+
})
221+
```

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /