45.vue3响应式原理

41 阅读3分钟

昨天更新了v2的手写版,今天更新一下v3的吧.其实和vue2实现的思路也是差不多的,但是在一些api的选择上和细节处理上优化了不少,他主要也分为3个部分,reactive,track,trigger

reactive - 响应式处理

我们这里是写一个简单的响应式,所以就省略了对目标对象的一些访问、删除、查询、设置的操作的劫持.着重分析一些set和get函数,因为这两个函数中涉及到依赖收集和派发更新的操作。分析的时候我们会删除部分代码,只分析主流程。,首先会使用Reflect.get进行求值, 然后判断是否是只读的,如果不是就调用track进行依赖收集, 更新响应式对象的属性的时候会触发set函数,set函数内部的主要步骤也是三个,首先获取这个属性的oldValue,然后通过Reflect.set对属性进行赋值操作,最后调用trigger进行派发更新

const isObject = (val) => val !== null && typeof val === 'object';
function reactive(target) {
  if (!isObject(target)) return;
  const handler = {
    get(target, key, receiver) {
      let result = Reflect.get(target, key, receiver);
      track(target, key);
      return result;
    },
    set(target, key, value, receiver) {
      let oldValue = Reflect.get(target, key, receiver);
      let result = true;
      if (oldValue !== value) {
        result = Reflect.set(target, key, value, receiver);
        trigger(target, key);
      }

      return result;
    }
  };

  return new Proxy(target, handler);
}

track - 依赖收集

在进行分析依赖收集的流程之前我们要先弄明白一个概念targetMap, 它是一个WeakMap的数据结构,主要用于存放用来存储原始数据->key->deps这样的一个映射关系.

let targetMap = new WeakMap();
function track(target, key) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);

  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }

  let deps = depsMap.get(key);
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }

  deps.add(activeEffect);
}

trigger - 派发更新

  • 首先获取当前target对应的依赖映射表,如果没有,说明这个target没有依赖,直接返回,否则进行下一步
  • 然后声明一个集合和一个向集合中添加元素的方法
  • 将当前key的所有依赖集合循环遍历,按照对应的方式运行
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) {
    return;
  }
  let deps = depsMap.get(key);
  if (deps) {
    deps.forEach((effect) => {
      effect();
    });
  }
}

effect - 副作用函数

当我们向effect中传递一个原始的函数的时候,会立即执行一次,这里简写一下,源码是2个参数,第二个参数是配置参数,用于控制第一个参数的调度行为。判断当前的函数是否已经是副作用函数,如果是就将获取当前响应式函数的原始函数。然后将参数传递给createReactiveEffect来创建响应式副作用函数。最后执行返回的响应式副作用函数。

let activeEffect = null;
function watchEffect(effect) {
  activeEffect = effect;
  effect();
  activeEffect = null;
}

ref

和reactive差不多

// 判断一个对象是不是对象 是的话就用reactive代理
const convert = (val) => (isObject(val) ? reactive(val) : val);
class RefImpl {
  constructor(rawValue) {
    this._rawValue = rawValue;
    this.__v__isRef = true;
    // 判断 _rawValue 是否是一个对象
    // 如果是对象调用reactive使用 proxy来代理
    // 不是返回 _rawValue 本身
    this._value = convert(rawValue);
  }
  get value() {
    track(this, 'value');
    return this._value;
  }
  set value(newValue) {
    if (newValue !== this._value) {
      this._rawValue = newValue;
      this._value = convert(this._rawValue);
      trigger(this, 'value');
    }
  }
}

function ref(rawValue) {
  return new RefImpl(rawValue);
}

测试


```const obj = reactive({
  name: 'qqq',
  age: 23
});

watchEffect(() => {
  console.log('执行副作用函数', obj.age);//执行副作用函数 23
  //执行副作用函数 24
});
obj.age++;