watch 实现

第一步

watch 这个方法并不在响应式模块中,他是运行时中的 api,但是它确实是通过响应式模块实现的

reactive.ts

1
2
3
4
5
...

export function isReactive(value) {
return !!((value && value[ReactiveFlags.IS_REACTIVE]));
}

ref.ts

1
2
3
4
5
...

export function isRef(value) {
return value && value.__v_isRef;
}

packages/reactivity/src/apiWatch.ts

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
import { ReactiveEffect } from './effect';
import { isFunction, isObject } from '@vue/shared';
import { isReactive } from './reactive';
import { isRef } from './ref';

export function watch(source, cb, options = {} as any) {
// watchEffect 也是基于 doWatch 实现的
return doWatch(source, cb, options);
}

function traverse(source, depth, currentDepth = 0, seen = new Set()) {
if (!isObject(source)) {
return source;
}

if (depth) {
if (depth <= currentDepth) {
return source;
}
currentDepth++;
}

if (seen.has(source)) {
return source;
}

for (const key in source) {
traverse(source[key], depth, currentDepth, seen);
}

return source;
}

function doWatch(source, cb, { deep, immediate }) {
const reactiveGetter = (source) => traverse(source, deep === false ? 1 : undefined);

// 产生一个可以给 reactiveEffect 来使用的 getter,需要对这个对象进行取值操作,会关联当前的reactiveEffect

let getter;
if (isReactive(source)) {
getter = () => reactiveGetter(source);
} else if (isRef(source)) {
getter = () => source.value;
} else if (isFunction(source)) {
getter = source;
}

let oldValue;
const job = () => {
const newValue = effect.run();
cb(newValue, oldValue);
oldValue = newValue;
};

const effect = new ReactiveEffect(getter, job);

if (cb) {
if (immediate) {
job();
} else {
oldValue = effect.run();
}
}
}