第一步
安装依赖: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; }
|