Vue3 uses Proxy objects to rewrite reactive systems. Compared with Vue2, it has the following differences:

  • Multiple layers of property nesting, where the next-level property is only processed during the access process (responsive performance improvement)
  • The default listens for dynamically added properties
  • The deletion of the default listening property
  • The default listens for the array index and length properties
  • Can be used as a separate module

Key APIS for responsive systems:

  • Reactive: Processes objects or arrays in a reactive manner
  • Ref: Responds to basic type data
  • ToRefs: Converts all attribute values of the proxy object into responsive data

reactive

  • Arguments can only be objects or arrays (as simple as using ref)
  • Modify properties, responsive. Reassignment, not reactive, needs to be reactive again
  • The resulting reactive object cannot be deconstructed (destruct with toRefs)
1. Check the argument and return if it is not an object or array. * 2. Proxy Proxy target * GET collects dependencies, which require recursive processing if the property value is an object. Returns the value of the processed property. * When the value of the set attribute changes, the update is triggered. If the new property value is an object, it needs to be processed recursively. Returns a Boolean. * deleteProperty An object property exists, and an update is triggered after a successful deletion. Returns a Boolean. *@param { object, array } target
 * @return { proxy }* /

const isObject = (val) = >val ! = =null && typeof val === "object";
const isArray = (val) = >
  Object.prototype.toString.call(val) === "[object Array]";
const hasOwn = (obj, key) = > Object.prototype.hasOwnProperty.call(obj, key);

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

export function reactive(target) {
  if(! isObject(target)) {return target;
  }

  const handler = {
    get(target, key, receiver) {
      // Collect dependencies
      track(target, key);
      const result = Reflect.get(target, key, receiver);
      return convert(result);
    },
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, receiver);
      if (value === oldValue) return true;

      const result = Reflect.set(target, key, convert(value), receiver);
      // Trigger the update
      trigger(target, key);
      return result;
    },
    deleteProperty(target, key, receiver) {
      const hasKey = hasOwn(target, key);
      const result = Reflect.deleteProperty(target, key, receiver);
      if (hasKey && result) {
        // Trigger the update
        trigger(target, key);
        console.log("delete", key);
      }
      returnresult; }};return new Proxy(target, handler);
}

/** * watchEffect calls the underlying method * in callback() to access the responsive object properties, collecting dependencies *@param { function } callback* /
let activeEffect = null;
export function effect(callback) {
  activeEffect = callback;
  callback();
  activeEffect = null;
}

/** * Collect dependencies *@param {object, array} target
 * @param {string} key* /
let targetMap = new WeakMap(a);export function track(target, key) {
  if(! activeEffect)return;
  let depsMap = targetMap.get(target);
  if(! depsMap) { depsMap =new Map(a); targetMap.set(target, depsMap); }let dep = depsMap.get(key);
  if(! dep) { dep =new Set(a); depsMap.set(key, dep); } dep.add(activeEffect); }/** * Trigger the update * find the callback set based on the target and key, and iterate through it. *@param {object, array} target
 * @param {string} key* /
export function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if(! depsMap)return;
  const dep = depsMap.get(key);
  if(! dep)return;
  dep.forEach((cb) = > {
    cb();
  });
}
Copy the code

ref

  • Parameter is the base type and internally creates an object with a value property that has a getter and setter.
  • Parameter is an object, which is equivalent to reactive(obj) returning a proxy object
  • The returned object, reassigned as an object, is also reactive.
/** * Responds to the original data type * 1. If it is an object with __v_isRef, return * 2. Reactive (value * 3); reactive (value * 3); The object returned, __v_isRef = true, the value attribute fetching collects dependencies, and the setting triggers updates. *@param {string, boolean, number} raw
 * @return {object}* /
export function ref(raw) {
  if (isObject(raw) && row.__v_isRef) return;

  let value = convert(raw);

  const r = {
    __v_isRef: true.get value() {
      track(r, "value");
      return value;
    },
    set value(newValue) {
      if(newValue ! == value) { value = convert(newValue); trigger(r,"value"); }}};return r;
}
Copy the code

toRefs

  • Proxy Indicates the proxy object
  • Turn all attribute values of the proxy object into responsive data. Can be deconstructed.
/** * change all attributes of the responsive object to ref *@param {*} proxy* /
export function toRefs(proxy) {
  const result = isArray(proxy) ? [] : {};
  for (let key in proxy) {
    result[key] = toRef(proxy, key);
  }
  return result;
}

/** * change the attribute specified by the responsive object to ref * 1. The returned object deconstruction is similar to that returned by ref * 2. There is no need to collect dependencies and trigger updates because the proxy is inherently reactive *@param {proxy} Proxy Proxy instance *@param {string} key* /
export function toRef(proxy, key) {
  return {
    __v_isRef: true.get value() {
      return proxy[key];
    },
    set value(newValue) { proxy[key] = newValue; }}; }Copy the code