# reactive 原理

我们以reactiveAPI 为例,其实本身 API 是很简单的,传入一个对象,返回一个 reactive 对象,创建的方法是createReactiveObject

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  );
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  // 前面都是一些对象是否已经proxy等的判断逻辑,这里就不展示了

  const observed = new Proxy(
    target,
    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
  );
  def(
    target,
    isReadonly ? ReactiveFlags.READONLY : ReactiveFlags.REACTIVE,
    observed
  );
  return observed;
}

那么这里最重要的就是new Proxy了,可以说理解 vue3 的响应式原理过程就是理解这个proxy创建的过程,而了解这个过程,主要就是看第二个参数,在这里就是collectionHandlers或者baseHandlers,大部分是后者,前者主要针对,Set、Map 等。

那么我们就来看baseHandlers

export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys,
};

可见 vue 代理了这几个操作,那么我们一个个看这几个操作做了啥:

function get(target: object, key: string | symbol, receiver: object) {
  // ...内部key的货足

  // 关于数组的一些特殊处理
  const targetIsArray = isArray(target);
  if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
    return Reflect.get(arrayInstrumentations, key, receiver);
  }

  // 获取请求值
  const res = Reflect.get(target, key, receiver);

  // ...如果是内部值的获取则直接返回res

  if (!isReadonly) {
    track(target, TrackOpTypes.GET, key);
  }

  // 返回的一些处理

  return res;
}

这里的重点就在于track(target, TrackOpTypes.GET, key);,这个是我们正常获取值的时候都会执行到的代码:

export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (!shouldTrack || activeEffect === undefined) {
    return;
  }
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect);
    activeEffect.deps.push(dep);
    if (__DEV__ && activeEffect.options.onTrack) {
      activeEffect.options.onTrack({
        effect: activeEffect,
        target,
        type,
        key,
      });
    }
  }
}

好了,重点来了,我们逐行分析。

第一个if,就是根据环境变量的shouldTrack来判断是否要进行跟踪,如果你已经看过processComponent的章节,那么你现在应该就是豁然开朗的感觉。因为在执行processComponent里面的setup的时候,我们特地关闭了track,而那时候就是把shouldTrack改为了false

let depsMap = targetMap.get(target)这行,targetMap是一个 map,用来记录所有的响应对象。之后如果目前没有记录该对象,那么就重新记录。

这个 target 对应的也是一个 map,他会对每个 key 建立一个 set。

最后要记录这次的 effect 了,这里所谓的effect是什么呢?就是当前正在调用这个对象的函数。比如:

const Comp = {
  setup() {
    const state = reactive({ a: 1 });
    return () => {
      return <div>{state.a}</div>;
    };
  },
};

在我们执行 return 的 render 方法的时候,我们调用了state.a,这个时候 proxy 就调用了track,那么这时候的activeEffect就是这个 render 方法。这里的effect就是,当state.a改动的时候,我们需要重新执行该 render 方法来进行渲染。

那么他是什么时候被设置的呢?在mount章节的时候我们提到了,在执行render方法的时候,我们执行这样一句代码instance.update = effect(function componentEffect()...),就是在这里调用的effect方法里面,把activeEffect记录为componentEffect,而这个componentEffect里面则运行了render方法。

是不是一下子就串起来了呢?有没有豁然开亮的感觉?有的话就关注一波吧。

另外这个getHandler里面有句代码也挺有意思的:

if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}

在获取属性的时候,如果返回的值是对象的时候,才会对其执行reactive,这也就是延迟处理,而且readonly的话是不执行reactive的。

OK,这里就把 reactive 的读取讲解完了,咱们接下来就讲讲set