vue/reactivity响应式包都干了什么

52 阅读3分钟

开门见山,先来一下尤雨溪的vue Mastery里面实现的最简单的响应式版本(思想一样,具体代码我小改一手,更好理解)

const trackMap = new WeakMap();
let activeEffect = null;
// obj => key => effect

const reactive = (obj) => {
  return new Proxy(obj, {
    get(target, key, receiver) {
      // track-跟踪
      trackMap.set(target, { [key]: { effects: [] } });
      const current = trackMap.get(target);
      current[key].effects.push(activeEffect);

      return Reflect.get(target, key, receiver);
    },
    set(target, key, value) {
      target[key] = value;
      // trigger-触发
      trackMap.get(target)?.[key]?.effects?.forEach((effect) => effect());
      return Reflect.set(target, key, value);
    },
  });
};

const effect = (effect) => {
  activeEffect = effect;
  effect();
  activeEffect = null;
};

let obj = reactive({ a: 1, b: 2 });

effect(() => {
  console.log(obj.a);
});

obj.a = 2;
obj.b = 3;

//print
1
2

很好理解,重点就是get的时候把函数effect函数收集起来,set时候再触发

但是真正的实现肯定不会这么简单,很多源码解析讲到这里就结束了,或者再给你讲点实现,但是我想告诉你的是,怎么从这个思想,到一整个响应式的包.

响应式的包一共有11个文件,看看都是什么

  1. 前两个文件都是处理函数,输出的是对应到proxy里面的一些方法例如: get,set,deleteProperty, has,ownKeys

这些方法会根据响应式对象的不同有很多变化,例如处理数组,处理循环嵌套,readonly和shallow浅层响应,

collectionHandlers, 处理map这种不同的对象

  1. computed和deferredComputed用来封装对外的计算属性api
  2. dep文件用来处理依赖相关的几个对象,还有effcect缓存的功能
  3. effect文件用来处理依赖收集和触发effect的代码, effectScope用来开启一个作用域手动控制effect的开始和失效,这是一个vue3.2的新功能
  4. operations用来定义了两个枚举对象,看一眼就知道干嘛用的了

export const enum TrackOpTypes {
  GET = 'get',
  HAS = 'has',
  ITERATE = 'iterate'
}

export const enum TriggerOpTypes {
  SET = 'set',
  ADD = 'add',
  DELETE = 'delete',
  CLEAR = 'clear'
}
  1. Reactive 和ref是平时最常用的两个api,用来组装功能并且对外暴露api使用

  2. Warning 用来拼接警告信息

这个包其实就是对响应式思想干了几件事

抽象 ! 组合 ! 处理边界条件! 代理更多的属性! 组装API

例如: baseHandler 就是为了输出这个方法以及对不同的对象类型进行不同的函数处理

export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}
// 只读对象处理器
export const readonlyHandlers: ProxyHandler<object> = {
  get: readonlyGet,
  set(target, key) {
    if (__DEV__) {
      warn(
        `Set operation on key "${String(key)}" failed: target is readonly.`,
        target
      )
    }
    return true
  },
  deleteProperty(target, key) {
    if (__DEV__) {
      warn(
        `Delete operation on key "${String(key)}" failed: target is readonly.`,
        target
      )
    }
    return true
  }
}
// 浅层响应处理器
export const shallowReactiveHandlers = /*#__PURE__*/ extend(
  {},
  mutableHandlers,
  {
    get: shallowGet,
    set: shallowSet
  }
)

// Props handlers are special in the sense that it should not unwrap top-level
// refs (in order to allow refs to be explicitly passed down), but should
// retain the reactivity of the normal readonly object.
export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
  {},
  readonlyHandlers,
  {
    get: shallowReadonlyGet
  }
) 

源码其实很好看懂,只是第一眼不知道每一块代码的目的是什么,或者是某一块的代码在处理什么问题,有人告诉你都是什么之后就很好调试了

希望看完的人把vue源码克隆下来,花一两个小时看一下,说不定什么时候调试就用到了