Vue3源码系列——Reactive

406 阅读5分钟

Reactive有那些可用的函数:

export {
  reactive,
  readonly,
  isReactive,
  isReadonly,
  isProxy,
  shallowReactive, 
  shallowReadonly,
  markRaw, 
  toRaw,
  ReactiveFlags, // 是type
  DeepReadonly, // 是type
  ShallowReactive, // 是type
  UnwrapNestedRefs // 是type
} from './reactive'

type和枚举

const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
  RAW = "__v_raw",
}
// 这三个都是用做type,就不讲了,有兴趣的去看一下代码呗
DeepReadonly, 
ShallowReactive,
UnwrapNestedRefs 

公共的部分(后续很多地方要回到这)

1、生成get、set

const get = createGetter();
const set = createSetter();

2、createGetter、createSetter

createGetter接受两个参数:1、不可修改;2、浅代理

下面代码有三个重点,已经标注,后续很多api会用到。

function createGetter(isReadonly = false, shallow = false) {
  return function get(target, key, receiver) {
    //** 重点一**
    const isExistInReactiveMap = () =>
      key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);
    const isExistInReadonlyMap = () =>
      key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);
    const isExistInShallowReadonlyMap = () =>
      key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    } else if (
      isExistInReactiveMap() ||
      isExistInReadonlyMap() ||
      isExistInShallowReadonlyMap()
    ) {
      return target;
    }
    //** 重点二**
    const res = Reflect.get(target, key, receiver);
    // 问题:为什么是 readonly 的时候不做依赖收集呢
    // readonly 的话,是不可以被 set 的, 那不可以被 set 就意味着不会触发 trigger
    // 所有就没有收集依赖的必要了
    // 只读的数据回改变,也就不需要收集依赖
    if (!isReadonly) {
      // 在触发 get 的时候进行依赖收集
      track(target, "get", key);
    }
    // shallow只劫持一层
    if (shallow) {
      return res;
    }
    if (isObject(res)) {
      // 把内部所有的是 object 的值都用 reactive 包裹,变成响应式对象
      // 如果说这个 res 值是一个对象的话,那么我们需要把获取到的 res 也转换成 reactive
      // res 等于 target[key]
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    // 在触发 set 的时候进行触发依赖
    //** 重点三**
    trigger(target, "get", key);
    return result;
  };
}

3、创建Reactive对象的函数

function getTargetType(value: Target) {
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value))
}
function createReactiveObject(target, proxyMap, baseHandlers) {
  // 核心就是 proxy
  // 目的是可以侦听到用户 get 或者 set 的动作
  // 如果命中的话就直接返回就好了
  // 使用缓存做的优化点
  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    return existingProxy;
  }
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy(target, baseHandlers);
  // 把创建好的 proxy 给存起来,
  proxyMap.set(target, proxy);
  return proxy;
}

reactive对对象进行深层代理

核心

  • get、set设置:get用公共的createGetter参数isReadonly, shallow没有传,都是undefined;set用公共的createSetter
  • 被代理过的数据做缓存reactiveMap
  • createReactiveObject创建
// get、set
const mutableHandlers = {
  get: createGetter(); 
  set: createSetter(),
};
// 缓存
const reactiveMap = new WeakMap();
function reactive(target) {
  createReactiveObject(target, reactiveMap, mutableHandlers);
}

细节

1、公共2部分createGetter函数,重点二部分

  • isReadonly没有传,则是undefined,会出发依赖收集
  • shallow没有传,则是undefined,不会执行return
  • res是对象,需要递归代理
if (!isReadonly) {
  track(target, "get", key);
}
if (shallow) {
  return res;
}
if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}

2、公共2部分createSetter函数,重点三部分

触发依赖

function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
  // 在触发 set 的时候进行触发依赖
  // 重点三
  trigger(target, "get", key);
  return result;
};

readonly只读代理是深层的,任何被访问的嵌套 property 也是只读的

核心

  • get、set设置:get用公共的createGetter参数isReadonly=true,shallow没有传,是undefined;set自定义
  • 被代理过的数据做缓存readonlyMap
  • createReactiveObject创建
// get、set
const readonlyGet = createGetter(true); 
const readonlyHandlers = {
  get: readonlyGet,
  set(target, key) {
    // readonly 的响应式对象不可以修改值
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true;
  },
};
// 缓存
const readonlyMap = new WeakMap();
function readonly(target) {
  return createReactiveObject(target, readonlyMap, readonlyHandlers);
}

细节

1、公共2部分createGetter函数,重点二部分

  • isReadonly=true,不需要依赖收集,only不允许修改
  • shallow没有传,则是undefined,不会执行return
  • res是对象的话,需要进行递归代理
if (!isReadonly) {
  track(target, "get", key);
}
if (shallow) {
  return res;
}
if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}

2、set自定义

没有收集依赖,也就不用触发,set时回报错

set(target, key) {
  // readonly 的响应式对象不可以修改值
  console.warn(
    `Set operation on key "${String(key)}" failed: target is readonly.`,
    target
  );
  return true;
},

shallowReactive跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换

核心

  • get、set设置:get用公共的createGetter参数isReadonly=fale,shallow=true;set用公共的createSetter
  • 被代理过的数据做缓存shallowMap
  • createReactiveObject创建
// get、set
const shallowGet = createGetter(false, true); 
const shallowHandlers = {
  get: shallowGet,
  set: createSetter(),
};
// 缓存
const shallowMap = new WeakMap();
function readonly(target) {
  return createReactiveObject(target, shallowMap, shallowHandlers);
}

细节

1、公共2部分createGetter函数,重点二部分

  • isReadonly=false,需要依赖收集
  • shallow=true,执行return res
  • 执行不到isObject(res),不进行递归代理
if (!isReadonly) {
  track(target, "get", key);
}
if (shallow) {
  return res;
}
if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}

2、set使用公共的

触发依赖

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  // 在触发 set 的时候进行触发依赖
  // 重点三
  trigger(target, "get", key);
  return result;
};

shallowReadonly使其自身的 property 为只读,但不执行嵌套对象的深度只读转换

核心

  • get、set设置:get用公共的createGetter参数isReadonly=true,shallow=true没有true;set自定义
  • 被代理过的数据做缓存shallowReadonlyMap
  • createReactiveObject创建
// get、set
const shallowReadonlyGet = createGetter(true, true);
shallowReadonlyHandlers = {
  get: shallowReadonlyGet,
  set(target, key) {
    // readonly 的响应式对象不可以修改值
    console.warn(
      `Set operation on key "${String(key)}" failed: target is readonly.`,
      target
    );
    return true;
  },
};
// 缓存
const shallowReadonlyMap = new WeakMap();
function reactive(target) {
  createReactiveObject(target, shallowReadonlyMap, mutableHandlers);
}

细节

1、公共2部分createGetter函数,重点二部分

  • isReadonly=true,不需要依赖收集
  • shallow=true,执行return res
  • 执行不到isObject(res),不进行递归代理
if (!isReadonly) {
  track(target, "get", key);
}
if (shallow) {
  return res;
}
if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}

2、set使用公共的

没有收集依赖,也就不用触发,set时回报错

set(target, key) {
  // readonly 的响应式对象不可以修改值
  console.warn(
    `Set operation on key "${String(key)}" failed: target is readonly.`,
    target
  );
  return true;
},

isReactive、isReadonly、isProxy

  • isReactive检查对象是否是由 reactive 创建的响应式代理。
  • isReadonly检查对象是否是由 readonly 创建的只读代理。
  • isProxy检查对象是否是由 reactivereadonly 创建的 proxy。

核心

公共2部分createGetter函数,重点一部分

 // 重点一
const isExistInReactiveMap = () =>
  key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);
const isExistInReadonlyMap = () =>
  key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);
const isExistInShallowReadonlyMap = () =>
  key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);
if (key === ReactiveFlags.IS_REACTIVE) {
  // isReactive的结果
  return !isReadonly;
} else if (key === ReactiveFlags.IS_READONLY) {
  // isReadonly的结果
  return isReadonly;
} else if (
  isExistInReactiveMap() ||
  isExistInReadonlyMap() ||
  isExistInShallowReadonlyMap()
) {
  // isProxy的结果
  return target;
}

函数源码

Reactive的get使用的createGetter(),参数isReadonly=undefined

  • 代理过:value[ReactiveFlags.IS_REACTIVE] = !undefined = true,return true
  • 未代理过:value[ReactiveFlags.IS_REACTIVE] = undefined,return false
export function isReactive(value) {
  return !!value[ReactiveFlags.IS_REACTIVE];
}

Readonly的get使用的createGetter(true); 参数isReadonly=true

  • 代理过:value[ReactiveFlags.IS_READONLY] = true,return true
  • 未代理过:value[ReactiveFlags.IS_READONLY] = undefined,return false
export function isReadonly(value) {
  return !!value[ReactiveFlags.IS_READONLY];
}

isProxy源码直接看,不用解释了

export function isProxy(value) {
  return isReactive(value) || isReadonly(value);
}

markRaw

标记一个对象,使其永远不会转换为 proxy。返回对象本身。

给对象做一个标记:key=ReactiveFlags.SKIP value=true 描述:可设置、不可枚举

function markRaw(value) {
  def(value, ReactiveFlags.SKIP, true)
  return value
}
const def = (obj, key, value) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}

通过公共3部分看到:当target[ReactiveFlags.SKIP]===true或者不可扩展时,直接return target

toRaw

返回 reactivereadonly 代理的原始对象。这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。建议保留对原始对象的持久引用。请谨慎使用。

toRaw结合公共2部分** 重点一**分析:

  • 未代理过直接return value
  • 代理过return target(target是代理时用的原对象)
function toRaw(value) {
  if (!value[ReactiveFlags.RAW]) {
    return value;
  }
  return value[ReactiveFlags.RAW];
}

const isExistInReactiveMap = () =>
      key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);
const isExistInReadonlyMap = () =>
  key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);
const isExistInShallowReadonlyMap = () =>
  key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);
if (key === ReactiveFlags.IS_REACTIVE) {
  return !isReadonly;
} else if (key === ReactiveFlags.IS_READONLY) {
  return isReadonly;
} else if (
  isExistInReactiveMap() ||
  isExistInReadonlyMap() ||
  isExistInShallowReadonlyMap()
) {
  return target;
}