8-实现 isProxy & shallowReadonly & shallowReactive

261 阅读2分钟

isProxy

思考:

  1. vue3 isProxy的作用是用来判断对象是否为reactive和readonly对象
  2. 我们已经实现了 isReactive 和 isReadonly 了,其实就是拿这两个做判断,比较简单

index.ts

+ export function isProxy(raw) {
+   // 双取反是为了兼容返回undefined
+   return isReadonly(raw) || isReactive(raw);
+ }

shallowReadonly & shallowReactive

思考

  1. 其实这两个都很类似,只做首层代理,不做深层的转换
  2. readonly 深层数据变化不会被拦截
  3. reacitve 深层双向绑定不会触发

回顾前面实现的代码,我们在Proxy get时,做了对象判断,对嵌套对象做了深度转换,以保证深度嵌套的数据能够被侦听(reactive)和拦截(readonly)

baseHandlers.ts - createdGetter

 * @Author: Lin zefan
 * @Date: 2022-03-16 18:30:25
 * @LastEditTime: 2022-03-20 11:55:59
 * @LastEditors: Lin zefan
 * @Description:
 * @FilePath: \mini-vue3\src\reactivity\baseHandlers.ts
 *
 */
 
// 有这么一行判断
if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
}

根据我们前面的思考的几个点,那我们是不是可以在这里加个标识,如果shallow为true,就不做深度转换。

实现

index.ts

/*
 * @Author: Lin zefan
 * @Date: 2022-03-15 13:08:22
 * @LastEditTime: 2022-03-20 11:07:51
 * @LastEditors: Lin zefan
 * @Description:
 * @FilePath: \mini-vue3\src\reactivity\index.ts
 *
 */
import {
  mutableHandles,
  readonlyHandles,
+  shallowReadonlyHandles,
+  shallowReactiveHandles
} from "./baseHandlers";

export const enum ReactiveEnum {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
}

function createdBaseHandler(raw, baseHandler) {
  return new Proxy(raw, baseHandler);
}

export function reactive(raw) {
  return createdBaseHandler(raw, mutableHandles);
}

export function readonly(raw) {
  return createdBaseHandler(raw, readonlyHandles);
}

+ export function shallowReactive(raw) {
+   return createdBaseHandler(raw, shallowReactiveHandles);
+ }

+ export function shallowReadonly(raw) {
+   return createdBaseHandler(raw, shallowReadonlyHandles);
+ }

export function isReadonly(raw) {
  // 双取反是为了兼容返回undefined
  return !!raw[ReactiveEnum.IS_READONLY];
}

export function isReactive(raw) {
  // 双取反是为了兼容返回undefined
  return !!raw[ReactiveEnum.IS_REACTIVE];
}

export function isProxy(raw) {
  // 双取反是为了兼容返回undefined
  return isReadonly(raw) || isReactive(raw);
}

baseHandlers.ts

/*
 * @Author: Lin zefan
 * @Date: 2022-03-16 18:30:25
 * @LastEditTime: 2022-03-20 11:55:59
 * @LastEditors: Lin zefan
 * @Description:
 * @FilePath: \mini-vue3\src\reactivity\baseHandlers.ts
 *
 */
import { reactive, ReactiveEnum, readonly } from ".";
import { extend, isObject } from "../shared";
import { track, trigger } from "./effect";

+ function createdGetter(isReadonly = false, shallow = false) {
  return function (target, key, receiver) {
    const res = Reflect.get(target, key, receiver);
    // 判断是否为reactive
    if (key === ReactiveEnum.IS_REACTIVE) {
      return !isReadonly;
    }
    // 判断是否为readonly
    if (key === ReactiveEnum.IS_READONLY) {
      return isReadonly;
    }
+    /** 嵌套转换判断, 思考
+     * 1. 如果shallow为true,那就不进行深度转换
+     * 2. 没有被深度转换的,是一个普通对象,不会二次转换
+     * 3. 即没有readonly深度拦截, 没有reactive的深度对象响应(没有被收集)
+     */
+    if (isObject(res) && !shallow) {
+      return isReadonly ? readonly(res) : reactive(res);
+    }

    // 如果是readonly,不会进行收集
    !isReadonly && track(target, key);

    return res;
  };
}

function createdSetter() {
  return function (target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver);
    trigger(target, key);
    return res;
  };
}

// 避免多次创建,这里直接用变量接收~
const get = createdGetter();
const set = createdSetter();
const readonlyGet = createdGetter(true);
+ const shallowReactiveGet = createdGetter(false, true);
+ const shallowReadonlyGet = createdGetter(true, true);

export const mutableHandles = {
  get,
  set,
};

export const readonlyHandles = {
  get: readonlyGet,
  set(target, key, value, receiver) {
    // 给一个警告
    console.warn(`${key}是只读的,因为被readonly包裹了`, target);
    return true;
  },
};

+ export const shallowReadonlyHandles = extend({}, readonlyHandles, {
+   get: shallowReadonlyGet,
+ });

+ export const shallowReactiveHandles = extend({}, mutableHandles, {
+   get: shallowReactiveGet,
+ });