reactive 实现

第一步

安装依赖:pnpm install @vue/shared --workspace --filter @vue/reactivity

packages/reactivity/src/reactive.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
import { isObject } from '@vue/shared';

export function reactive(target) {
return createReactiveObject(target);
}

export const mutableHandlers: ProxyHandler<any> = {
// 属性访问器
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
return res;
},

set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
return result;
}
};

function createReactiveObject(target) {
// 必须是对象才行,否则不做代理
if (!isObject(target)) {
return target;
}

const proxy = new Proxy(target, mutableHandlers);
return proxy;
}

第二步

第一步中只是简单的对对象进行代理,下面考虑对一个对象同时代理两次的情况,即定义了一个对象,两次调用 reactive 方法传递的都是这个对象,如:

1
2
3
4
const obj = { name: 'xiaosheng' }

const state1 = reactive(obj)
const state2 = reactive(obj)

packages/reactivity/src/reactive.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
import { isObject } from '@vue/shared';

export function reactive(target) {
return createReactiveObject(target);
}
// -----------------------------新增开始-----------------------------
const reactiveMap = new WeakMap();
// -----------------------------新增结束-----------------------------

export const mutableHandlers: ProxyHandler<any> = {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
return res;
},

set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
return result;
}
};

function createReactiveObject(target) {
// 必须是对象才行,否则不做代理
if (!isObject(target)) {
return target;
}

// -----------------------------新增开始-----------------------------
const isExistProxy = reactiveMap.get(target);
if (isExistProxy) {
return isExistProxy;
}
// -----------------------------新增结束-----------------------------

const proxy = new Proxy(target, mutableHandlers);
// -----------------------------新增开始-----------------------------
reactiveMap.set(target, proxy);
// -----------------------------新增结束-----------------------------
return proxy;
}

第三步

接着上面的步骤,这次考虑的情况是:当将一个对象代理后,将代理后的对象再次传递给 reactive 方法,如下代码,state1 已经是代理后的对象,那么在第二次调用 reactive 方法的时候,就不应该再次进行代理

1
2
3
4
const obj = { name: 'xiaosheng' }

const state1 = reactive(obj)
const state2 = reactive(state1)

packages/reactivity/src/reactive.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
import { isObject } from '@vue/shared';

export function reactive(target) {
return createReactiveObject(target);
}

// -----------------------------新增开始-----------------------------
enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
}
// -----------------------------新增结束-----------------------------

const reactiveMap = new WeakMap();

export const mutableHandlers: ProxyHandler<any> = {
get(target, key, receiver) {
// -----------------------------新增开始-----------------------------
if (key === ReactiveFlags.IS_REACTIVE) {
return true;
}
// -----------------------------新增结束-----------------------------
const res = Reflect.get(target, key, receiver);
return res;
},

set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
return result;
}
};

function createReactiveObject(target) {
// 必须是对象才行,否则不做代理
if (!isObject(target)) {
return target;
}

// -----------------------------新增开始-----------------------------
if (target[ReactiveFlags.IS_REACTIVE]) {
return target;
}
// -----------------------------新增结束-----------------------------

const isExistProxy = reactiveMap.get(target);
if (isExistProxy) {
return isExistProxy;
}

const proxy = new Proxy(target, mutableHandlers);
reactiveMap.set(target, proxy);
return proxy;
}

注意:这个位置可能稍微有点绕,我仍然记得当初第一次看到这里的时候,是一种似懂非懂的状态

对象第一次代理的时候,由于他不是 proxy,所以就在 target[ReactiveFlags.IS_REACTIVE] 取属性的时候,是不会走属性访问器的,因为他只是一个普通的对象而已

但是当第二次进来的时候,由于他已经是 proxy 了,所以在取该属性的时候,是会走 proxy 中的属性访问器,同时,这时候取的属性是 ReactiveFlags.IS_REACTIVE,直接返回 true,因为他既然能进入到属性访问器中,那么他肯定是已经被代理过的,所以就直接返回当前的 target 即可

第四步

整理代码:

packages/reactivity/src/baseHandler.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { reactive } from './reactive';
import { ReactiveFlags } from './constants';

export const mutableHandlers: ProxyHandler<any> = {
get(target, key, receiver) {
if (key === ReactiveFlags.IS_REACTIVE) {
return true;
}
const res = Reflect.get(target, key, receiver);
return res;
},

set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
return result;
}
};

packages/reactivity/src/constants.ts

1
2
3
export enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive'
}

packages/reactivity/src/reactive.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
import { isObject } from '@vue/shared';
import { mutableHandlers } from './baseHandler';
import { ReactiveFlags } from './constants';

const reactiveMap = new WeakMap();

export function reactive(target) {
return createReactiveObject(target);
}

function createReactiveObject(target) {
if (!isObject(target)) {
return target;
}
const isExistProxy = reactiveMap.get(target);
if (isExistProxy) {
return isExistProxy;
}

if (target[ReactiveFlags.IS_REACTIVE]) {
return target;
}

const proxy = new Proxy(target, mutableHandlers);
reactiveMap.set(target, proxy);
return proxy;
}