@vue/reactivity

This section implements responsive related apis, including the following

@vue/reactivity/src/index.ts

export { ref, shallowRef, toRef, toRefs } from "./ref";
export { effect } from "./effect";
export { computed } from "./computed";
export {
  reactive,
  shallowReactive,
  readonly,
  shallowReadonly,
} from "./reactive";
Copy the code

Implement reactive

package.json

Configure package options, package out of the format of ESM – Bundler, ESM – Browser, CJS,global

// package.json
{
  "name": "@vue/reactivity"."version": "0.1.0 from"."main": "index.js"."buildOptions": {
    "name": "VueReactivity"."formats": ["esm-bundler"."esm-browser"."cjs"."global"]}}Copy the code

reactive.ts

Reactive API, reactive, shallowReactive, readonly,shallowReadonly are created using the createReactiveObject function. Target, isReadonly, baseHandlers

The handler configuration of proxy Reactive, shallowReactive, readonly, and shallowReadonly is extracted into baseHandlers.ts

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, false, shallowReadonlyHandlers);
}

const readonlyMap = new WeakMap(a);const reactiveMap = 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);

  return proxy;
}
Copy the code

baseHandlers.ts

import { isObject, extend, isArray, isIntegerKey, hasOwn, hasChanged } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive, readonly } from "./reactive";

function createGetter(isReadonly = false, shallow = false) {
  return function get(target, key, receiver) {
    const res = Reflect.get(target, key, receiver);
    if(! isReadonly) {// Do dependency collection
      track(target, TrackOpTypes.GET, key);
    }

    if (shallow) {
      return res;
    }

    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  };
}

function createSetters(shallow = false) {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key);

    const result = Reflect.set(target, key, value, receiver);

    if(! hadKey) { trigger(target, TriggerOpTypes.ADD, key, value); }else if (hasChanged(oldValue, value)) {
      trigger(target, TriggerOpTypes.SET, key, value, oldValue);
    }

    return result;
  };
}

const get = createGetter();
const set = createSetters();
const shallowGet = createGetter(false.true);
const shallowSet = createSetters(true);
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true.true);
const readonlyObj = {
  set(target, key) {
    console.warn(
      `Set operation on key "The ${String(key)}" failed: target is readonly.`, target ); }};export const mutableHandlers = {
  get,
  set,
};

export const shallowReactiveHandlers = {
  get: shallowGet,
  set: shallowSet,
};

export const readonlyHandlers = extend(
  {
    get: readonlyGet,
  },
  readonlyObj
);

export const shallowReadonlyHandlers = extend(
  {
    get: shallowReadonlyGet,
  },
  readonlyObj
);
Copy the code

In baseHandlers. Ts createGetter and createSetter are used to create pairs of reactive and shallowReactive. Readonly,shallowReadonly Getters and setters for the four apis

The createGetter function does dependency collection for attributes of the accessed object, and the createSetter function does dependency collection and trigger updates for attributes of the accessed object, which we’ll talk about later

Realize the ref

import { hasChanged, isArray, isObject } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive } from "./reactive";

export function ref(value) {
  return createRef(value);
}

export function shallowRef(value) {
  return createRef(value, true);
}

export function toRef(target, key) {
  return new ObjectRefImpl(target, key);
}

export function toRefs(object) {
  const ret = isArray(object)?new Array(object.length) : {};
  for (let key in object) {
    ret[key] = toRef(object, key);
  }
  return ret;
}

function createRef(rawValue, shallow = false) {
  return new RefImpl(rawValue, shallow);
}

const convert = (val) = > (isObject(val) ? reactive(val) : val);

// Ref was an object before the beta, but was changed to a class because objects were not easy to extend
class RefImpl {
  public _value;
  public __v_isRef = true;

  constructor(public rawValue, public shallow) {
    // If it is deep, you need to make the inside of it responsive
    this._value = shallow ? rawValue : convert(rawValue);
  }

  get value() {
    // Depending on the collection, key is a fixed value
    track(this, TrackOpTypes.GET, "value");

    return this._value;
  }

  set value(newValue) {
    // setters only handle changes to the value property
    if (hasChanged(newValue, this.rawValue)) {
      this.rawValue = newValue;
      this._value = this.shallow ? newValue : convert(newValue);

      // send notifications
      trigger(this, TriggerOpTypes.SET, "value", newValue); }}}class ObjectRefImpl {
  public __v_isRef = true;
  constructor(public target, public key) {}
  get value() {
    return this.target[this.key];
  }
  set value(newValue) {
    this.target[this.key] = newValue; }}Copy the code

Ref takes an internal value and returns a reactive and mutable REF object. The ref object has a single property.value pointing to an internal value.

Ref is an API for designing responses to values of underlying types. Ref can also pass in an object, and internally respond to this object. The REF API returns an instance of RefImpl by calling createRef. The.value on this instance is the value passed in by the ref function. Dependencies are collected in the getter during the access phase, and dependency updates are triggered when modified

ToRef, however, does not have the ability to reuse refs (depending on collection, triggering updates), but simply creates a new REF for a property on the source responsive object. This ref can be understood as a reference, just a reactive join for the property of the source reactive object, with no reactive expression of its own.

RoRefs is the ability to reuse toRef, toRef the traversable properties of an object