手寫 Vue3.0 源碼——響應式 api

@vue/reactivity

本節實現響應式相關 api,包括以下vue

@vue/reactivity/src/index.tsreact

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

實現 reactive

package.jsontypescript

配置打包選項,打包出來的格式有 esm-bundler, esm-browser,cjs,globaljson

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

reactive.tsapi

實現響應式 api,reactive, shallowReactive, readonly,shallowReadonly 都使用 createReactiveObject 函數進行建立,該函數入參以下: target(目標對象), isReadonly(是否只讀), baseHandlers(proxy 的 handler 配置)ide

這裏將reactive, shallowReactive, readonly,shallowReadonly 的 proxy 的 handler 配置所有提取到 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();
const reactiveMap = new WeakMap();

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;
}

baseHandlers.tsui

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) {
      // 進行依賴收集
      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 "${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
);

baseHandlers.tscreateGettercreateSetter 函數用於建立對 reactive, shallowReactive, readonly,shallowReadonly四個 api 的 gettersetterthis

createGetter 函數會對訪問對象的屬性進行依賴收集,createSetter 函數會對訪問對象的屬性進行觸發更新,關於依賴收集和觸發更新咱們後續會講到設計

實現 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);

// beta 版本 以前的版本ref 就是個對象 ,因爲對象不方便擴展 改爲了類
class RefImpl {
  public _value;
  public __v_isRef = true;

  constructor(public rawValue, public shallow) {
    // 若是是深度的,須要把裏面的變成響應式的
    this._value = shallow ? rawValue : convert(rawValue);
  }

  get value() {
    // 依賴收集,key 爲固定的 value
    track(this, TrackOpTypes.GET, "value");

    return this._value;
  }

  set value(newValue) {
    // setter,只處理 value 屬性的修改
    if (hasChanged(newValue, this.rawValue)) {
      this.rawValue = newValue;
      this._value = this.shallow ? newValue : convert(newValue);

      // 派發通知
      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;
  }
}

ref 接受一個內部值並返回一個響應式且可變的 ref 對象。ref 對象具備指向內部值的單個 property .value

ref 本是用於設計對基礎類型的值進行響應式的一個 api,源碼中看到 ref 也能夠傳入一個對象,內部會對這個對象進行響應式。ref api經過調用 createRef 返回一個 RefImpl 實例,該實例上的 .value 就是 ref 函數傳入的值, 在訪問階段 getter 中會進行依賴收集,修改時會觸發依賴更新

而後 toRef 並無複用 ref 的能力(依賴收集、觸發更新),只是負責爲源響應式對象上的某個 property 新建立一個 ref。這個 ref 能夠理解爲引用,只是爲源響應式對象的 property 進行一個響應式鏈接,自身沒有響應式。

roRefs 就是複用 toRef 的能力,對一個對象的可遍歷屬性進行 toRef

相關文章
相關標籤/搜索