Skip to content

Commit 6b74b35

Browse files
committed
54-实现 watchEffect
1 parent ef0f4ef commit 6b74b35

File tree

4 files changed

+107
-2
lines changed

4 files changed

+107
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110

111111
- [x] 53-实现 monorepo
112112

113-
- [ ] 54-实现 watchEffect
113+
- [x] 54-实现 watchEffect
114114

115115
### 课程地址
116116

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { reactive } from "@guide-mini-vue/reactivity"
2+
import { watchEffect } from "../src/apiWatch";
3+
import { nextTick } from "../src/scheduler";
4+
import { vi } from "vitest";
5+
6+
7+
describe("api: watch", () => {
8+
it("effect", async() => {
9+
const state = reactive({ count: 0 });
10+
let dummy;
11+
watchEffect(() => {
12+
dummy = state.count;
13+
})
14+
expect(dummy).toBe(0)
15+
16+
state.count++
17+
await nextTick();
18+
expect(dummy).toBe(1)
19+
})
20+
21+
it("stopping the watcher (effetc)", async() => {
22+
const state = reactive({ count: 0 });
23+
let dummy;
24+
const stop: any = watchEffect(() => {
25+
dummy = state.count;
26+
})
27+
expect(dummy).toBe(0)
28+
29+
stop()
30+
31+
state.count++
32+
await nextTick();
33+
// should not be update
34+
expect(dummy).toBe(0)
35+
})
36+
37+
it("cleanup registration (effect)", async() => {
38+
const state = reactive({ count: 0 });
39+
const cleanup = vi.fn();
40+
let dummy;
41+
const stop: any = watchEffect((onCleanup) => {
42+
onCleanup(cleanup)
43+
dummy = state.count;
44+
})
45+
expect(dummy).toBe(0)
46+
47+
state.count++
48+
await nextTick();
49+
expect(cleanup).toHaveBeenCalledTimes(1);
50+
expect(dummy).toBe(1)
51+
52+
stop()
53+
expect(cleanup).toHaveBeenCalledTimes(2);
54+
})
55+
})

packages/runtime-core/src/apiWatch.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ReactiveEffect } from "../../reactivity/src/effect";
2+
import { queueFlushCb } from "./scheduler";
3+
4+
5+
export function watchEffect(source) {
6+
7+
function job() {
8+
effect.run()
9+
}
10+
11+
let cleanup;
12+
const onCleanup = function(fn) {
13+
cleanup = effect.onStop = () => {[
14+
fn()
15+
]}
16+
}
17+
18+
function getter() {
19+
if (cleanup) {
20+
cleanup();
21+
}
22+
source(onCleanup)
23+
}
24+
25+
const effect = new ReactiveEffect(getter, () => {
26+
queueFlushCb(job)
27+
})
28+
29+
effect.run()
30+
31+
return () => {
32+
effect.stop()
33+
}
34+
}

packages/runtime-core/src/scheduler.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
const queue: any[] = []
3+
const activePreFlushCbs: any[] = []
34

45
let isFlushPending = false
56

@@ -24,10 +25,25 @@ function queueFlush() {
2425
nextTick(flushJobs)
2526
}
2627

28+
export function queueFlushCb(job) {
29+
activePreFlushCbs.push(job);
30+
31+
queueFlush()
32+
}
33+
2734
function flushJobs() {
2835
isFlushPending = false
36+
37+
flushPreFlushCbs()
38+
2939
let job;
3040
while ((job = queue.shift()) !== undefined) {
3141
job()
3242
}
33-
}
43+
}
44+
45+
function flushPreFlushCbs() {
46+
for (let i = 0; i < activePreFlushCbs.length; i++) {
47+
activePreFlushCbs[i]()
48+
}
49+
}

0 commit comments

Comments
 (0)