LOADING

vue源码

2024/9/22
const obj = {
  a: 1,
  b: 2,
  c: {
    name: "张三",
    age: 18,
  },
};

const proxyObj = reactive(obj);


export function reactive(target) {
  const proxy = new Proxy(target, handlers);
  return proxy;
}

//delete
export default function (target, key) {
  // 判断一下目标对象上面是否有要删除的属性
  const hadKey = target.hasOwnProperty(key);

  // 进行删除行为
  const result = Reflect.deleteProperty(target, key);

  // 派发更新之前,需要判断一下
  if(hadKey && result){
    trigger(target, TriggerOpTypes.DELETE, key);
  }

  return result;
}


//get
import track from "../../effect/track.js";
import { TrackOpTypes, isObject } from "../../utils.js";
import { reactive } from "../../reactvie.js";

export default function (target, key) {
  // 拦截到 get 操作后,要做一些额外的事情
  // 要做的事情,就是收集依赖
  track(target, TrackOpTypes.GET, key);

  const result = Reflect.get(target, key);

  // 获取到的成员可能是对象,需要递归处理,将其转换为响应式
  if (isObject(result)) {
    return reactive(result);
  }

  return result;
}


//has
import track from "../../effect/track.js";
import { TrackOpTypes } from "../../utils.js";

export default function (target, key) {
  // 需要进行依赖的收集
  track(target, TrackOpTypes.HAS, key);
  return Reflect.has(target, key);
}

//ownkeys
import track from "../../effect/track.js";
import { TrackOpTypes } from "../../utils.js";
export default function (target) {
  // 在此之前,因为涉及到成员的读取,所以要进行依赖收集
  track(target, TrackOpTypes.ITERATE);

  return Reflect.ownKeys(target);
}


//set
import trigger from "../../effect/trigger.js";
import { TriggerOpTypes, hasChanged } from "../../utils.js";

export default function (target, key, value) {
  // 关于具体的操作类型需要进一步判断
  // 有可能是设置,有可能是新增
  const type = target.hasOwnProperty(key)
    ? TriggerOpTypes.SET
    : TriggerOpTypes.ADD;

  // 在设置之前需要缓存一下旧值
  const oldValue = target[key];

  // 先进行设置操作
  const result = Reflect.set(target, key, value);

  // 要不要派发更新需要一些判断
  if (hasChanged(oldValue, value)) {
    // 派发更新
    trigger(target, type, key);
  }

  return result;
}

utils

// 提供工具方法的文件

/**
 * 判断是否是对象
 * @param {*} target 要判断的值
 * @returns
 */
export function isObject(target) {
  return typeof target === "object" && target !== null;
}

/**
 * 判断值是否改变
 * @param {*} oldValue
 * @param {*} newValue
 * @returns
 */
export function hasChanged(oldValue, newValue) {
  // 使用该方法可以规避一些特殊的情况
  // NaN === NaN 在 JS 中是 false,Object.is 返回的是 true
  // +0 === -0 在 JS 中是 true,Object.is 返回的是 false
  return !Object.is(oldValue, newValue);
}

/**
 * 收集依赖的操作类型
 */
export const TrackOpTypes = {
  GET: "get",
  HAS: "has",
  ITERATE: "iterate",
};

/**
 * 触发器的操作类型
 */
export const TriggerOpTypes = {
  SET: "set",
  ADD: "add",
  DELETE: "delete",
};

/**
 * 这是一个特殊标识
 */
export const RAW = Symbol("raw");

export const ITERATE_KEY = Symbol("iterate");

effect

/**
 * 用于记录当前活动的 effect
 */
export let activeEffect = undefined;
export const targetMap = new WeakMap(); // 用来存储对象和其属性的依赖关系
const effectStack = [];

/**
 * 该函数的作用,是执行传入的函数,并且在执行的过程中,收集依赖
 * @param {*} fn 要执行的函数
 */
export function effect(fn, options = {}) {
  const { lazy = false } = options;
  const environment = () => {
    try {
      activeEffect = environment;
      effectStack.push(environment);
      cleanup(environment);
      return fn();
    } finally {
      effectStack.pop();
      activeEffect = effectStack[effectStack.length - 1];
    }
  };
  environment.deps = [];
  environment.options = options;
  if (!lazy) {
    environment();
  }
  return environment;
}

export function cleanup(environment) {
  let deps = environment.deps; // 拿到当前环境函数的依赖(是个数组)
  if (deps.length) {
    deps.forEach((dep) => {
      dep.delete(environment);
    });
    deps.length = 0;
  }
}

track

import { TrackOpTypes, ITERATE_KEY } from "../utils.js";
import { activeEffect, targetMap } from "./effect.js";

let shouldTrack = true; // 控制是否需要进行依赖收集的开关

/**
 * 暂停依赖收集
 */
export function pauseTracking() {
  shouldTrack = false;
}

/**
 * 恢复依赖收集
 */
export function resumeTracking() {
  shouldTrack = true;
}

/**
 * 收集器:用于收集依赖
 * @param {*} target 原始对象
 * @param {*} type 进行的操作类型
 * @param {*} key 针对哪一个属性
 */
export default function (target, type, key) {
  // 先进行开关状态的判断
  if (!shouldTrack || !activeEffect) {
    return;
  }

  // 这里要做的事情其实很简单,就是一层一层的去找,找到了就存储
  let propMap = targetMap.get(target);
  if (!propMap) {
    propMap = new Map();
    targetMap.set(target, propMap);
  }

  // 之前如果是遍历所有的属性, key 会是 undefined
  // 所以对 key 值做一下参数归一化
  if (type === TrackOpTypes.ITERATE) {
    key = ITERATE_KEY;
  }

  let typeMap = propMap.get(key);
  if (!typeMap) {
    typeMap = new Map();
    propMap.set(key, typeMap);
  }

  // 最后一步,根据 type 值去找对应的 Set
  let depSet = typeMap.get(type);
  if (!depSet) {
    depSet = new Set();
    typeMap.set(type, depSet);
  }

  // 现在找到 set 集合了,就可以存储依赖了
  if (!depSet.has(activeEffect)) {
    depSet.add(activeEffect);
    activeEffect.deps.push(depSet); // 将集合存储到 deps 数组里面
  }
}

trigger

// 这是触发器
import { TriggerOpTypes, TrackOpTypes, ITERATE_KEY } from "../utils.js";
import { targetMap, activeEffect } from "./effect.js";

// 定义修改数据和触发数据的映射关系
const triggerTypeMap = {
  [TriggerOpTypes.SET]: [TrackOpTypes.GET],
  [TriggerOpTypes.ADD]: [
    TrackOpTypes.GET,
    TrackOpTypes.ITERATE,
    TrackOpTypes.HAS,
  ],
  [TriggerOpTypes.DELETE]: [
    TrackOpTypes.GET,
    TrackOpTypes.ITERATE,
    TrackOpTypes.HAS,
  ],
};

/**
 * 触发器
 * @param {*} target 原始对象
 * @param {*} type 操作的类型
 * @param {*} key 操作的属性
 */
export default function (target, type, key) {
  // 要做的事情很简单,就是找到依赖,然后执行依赖
  const effectFns = getEffectFns(target, type, key);
  if (!effectFns) return;
  for (const effectFn of effectFns) {
    if (effectFn === activeEffect) continue;
    if (effectFn.options && effectFn.options.shcheduler) {
      // 说明用户传递了回调函数,用户期望自己来处理依赖的函数
      effectFn.options.shcheduler(effectFn);
    } else {
      // 执行依赖函数
      effectFn();
    }
  }
}

/**
 * 根据 target、type、key 这些信息找到对应的依赖函数集合
 * @param {*} target
 * @param {*} type
 * @param {*} key
 */
function getEffectFns(target, type, key) {
  const propMap = targetMap.get(target);
  if (!propMap) return;

  // 如果是新增或者删除操作,会涉及到额外触发迭代
  const keys = [key];
  if (type === TriggerOpTypes.ADD || type === TriggerOpTypes.DELETE) {
    keys.push(ITERATE_KEY);
  }

  const effectFns = new Set(); // 用于存储依赖的函数

  for (const key of keys) {
    const typeMap = propMap.get(key);
    if (!typeMap) continue;

    const trackTypes = triggerTypeMap[type];
    for (const trackType of trackTypes) {
      const dep = typeMap.get(trackType);
      if (!dep) continue;
      for (const effectFn of dep) {
        effectFns.add(effectFn);
      }
    }
  }
  return effectFns;
}