Reactive, shallowReactive, readonly, and shallowReadonly differ only in read-only, depth -> currization

Use of the reactivity module

<script src=".. /node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<script>
  const { reactive, shallowReactive, readonly, shallowReadonly } =
    VueReactivity;
  const state = reactive({ name: "cherish".age: { age: 10}}); state.age =12;
  console.log(state.age);
</script>
Copy the code

Implement four core apis

Open the sourceMap

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNEXT"."module": "ESNEXT"."strict": false."esModuleInterop": true."skipLibCheck": true."forceConsistentCasingInFileNames": true."moduleResolution": "node"."baseUrl": "."."paths": {
      "@vue/*": ["packages/*/src"+]}"sourceMap": true}}Copy the code

The directory structure

├ ─ ─ example │ └ ─ ─1.Reactivity - API. HTML ├ ─ ─ package. The json ├ ─ ─ packages │ ├ ─ ─ reactivity │ │ ├ ─ ─ package. The json │ │ └ ─ ─ the SRC │ │ ├ ─ ─ │ ├── ├─ ├─ ├─ exercises, exercises, exercises, exercises, exercises, exercises, exercises ├─ ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txt ├─ class.txtCopy the code

Entry into

packages/reactivity/src/index.js

export {
  reactive,
  shallowReactive,
  shallowReadonly,
  readonly,
} from "./reactive";
Copy the code

Corrified resolution

packages/reactivity/src/reactive.js

Extract the common createReactiveObject method

  1. Reactive intercepts only object types (ref can be a normal type).
  2. An object that has been proxied is no longer proxied (it is possible that an object is proxied deep and is proxied only).
  3. Make a map to see if the object has been proxied. WeakMap automatically garbage collects, does not cause memory leaks, and stores keys that are only objects.
import { isObject } from "@vue/shared";
import {
  mutableHandlers,
  shallowReactiveHandlers,
  readonlyHandlers,
  shallowReadonlyHandlers,
} from "./baseHandlers";
export function reactive(target) {
  return createReactiveObject(target, false, mutableHandlers);
}
export function shallowReactive(target) {
  return createReactiveObject(target, false, shallowReactiveHandlers);
}

export function readonly(target) {
  return createReactiveObject(target, true, readonlyHandlers);
}

export function shallowReadonly(target) {
  return createReactiveObject(target, true, shallowReadonlyHandlers);
}

const reactiveMap = new WeakMap(a);const readonlyMap = new WeakMap(a);export function createReactiveObject(target, isReadonly, baseHandlers) {
  if(! isObject(target))return target;
  const proxyMap = isReadonly ? readonlyMap : reactiveMap;
  const existProxy = proxyMap.get(target);
  if (existProxy) return existProxy;
  const proxy = new Proxy(target, baseHandlers);
  proxyMap.set(target, proxy); // Cache the object to be proxied and the corresponding proxied result

  return proxy;
}
Copy the code

baseHandlers.ts

import { isObject } from "@vue/shared/src";

import { reactive, readonly } from "./reactive";
debugger;
const createGetter =
  (isReadonly = false, shallow = false) = >
  (target, key, receiver) = > {
    const res = Reflect.get(target, key, receiver);
    if(! isReadonly) {// Collect dependencies and update views as data changes
      console.log("Value when effect is executed"."Collecting effect");
    }
    if (shallow) return res;
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  };
const createSetter =
  (shallow = false) = >
  (target, key, value, receiver) = > {
    const result = Reflect.set(target, key, value, receiver);
    return result;
  };

const get = createGetter();
const shallowGet = createGetter(false.true);
const readonlyGet = createGetter(true);
const showllowReadonlyGet = createGetter(true.true);
const set = createSetter();
const shallowSet = createSetter(true);
const readonlySet = {
  set: (_, key) = > console.warn(`set on key ${key} falied`),};export const mutableHandlers = { get, set };
export const shallowReactiveHandlers = { get: shallowGet, set: shallowSet };
export const readonlyHandlers = { get: readonlyGet, set: readonlySet };
export const shallowReadonlyHandlers = {
  get: showllowReadonlyGet,
  set: readonlySet,
};
Copy the code

Reflect the benefits of

  • subsequentObjectWill be migrated toReflect, such asReflect.getPropertyOf()
  • beforetarget[key] = valueMethod may fail to set a value, no exception is reported, and no value identifier is returned.ReflectMethod returns a value telling the user whether the setting was successful.

Ps. Reflect can be used without proxy

Vue2 & VUe3 comparison

  • vue2: Recurse from the beginning
  • vue3: Proxy only when the value is specified — lazy proxy

Design idea

Cremation, composition is better than inheritance