vue3 reactivity库简单实现

67 阅读2分钟

Vue 3 的响应式系统是其核心特性之一,它通过 reactivity 库实现。这个库提供了 reactiverefcomputedeffect 等 API。

核心概念

  1. Reactive: 将对象转换为响应式对象。
  2. Ref: 将基本类型值包装为响应式对象。
  3. Computed: 创建基于其他响应式数据的计算属性。
  4. Effect: 副作用函数,当响应式数据变化时自动执行。

源码结构

以下是 Vue 3 reactivity 库的核心实现,包括 reactiverefeffect 的简化代码。

依赖收集和触发

let activeEffect = null;

class Dep {
  constructor() {
    this.subscribers = new Set();
  }

  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }

  notify() {
    this.subscribers.forEach(sub => sub());
  }
}

function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

Dep 类用于依赖收集和触发更新。effect 函数用于注册副作用函数。

Reactive 实现

const targetMap = new WeakMap();

function getDep(target, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Dep();
    depsMap.set(key, dep);
  }
  return dep;
}

function reactive(target) {
  return new Proxy(target, {
    get(obj, key) {
      const dep = getDep(obj, key);
      dep.depend();
      return Reflect.get(obj, key);
    },
    set(obj, key, value) {
      const dep = getDep(obj, key);
      const result = Reflect.set(obj, key, value);
      dep.notify();
      return result;
    }
  });
}

reactive 函数通过 Proxy 将对象转换为响应式对象。通过 getDep 函数获取或创建依赖对象,并在 getset 拦截器中进行依赖收集和触发更新。

Ref 实现

class RefImpl {
  constructor(value) {
    this._value = value;
    this.dep = new Dep();
  }

  get value() {
    this.dep.depend();
    return this._value;
  }

  set value(newValue) {
    if (newValue !== this._value) {
      this._value = newValue;
      this.dep.notify();
    }
  }
}

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

ref 函数将基本类型值包装为响应式对象,通过 RefImpl 类实现。

Computed 实现

class ComputedRefImpl {
  constructor(getter) {
    this.getter = getter;
    this.dep = new Dep();
    this._dirty = true;
    this._value = undefined;
    this.effect = effect(() => {
      if (this._dirty) {
        this._value = this.getter();
        this._dirty = false;
      }
    });
  }

  get value() {
    if (this._dirty) {
      this._value = this.getter();
      this._dirty = false;
    }
    this.dep.depend();
    return this._value;
  }
}

function computed(getter) {
  return new ComputedRefImpl(getter);
}

computed 函数创建基于其他响应式数据的计算属性,通过 ComputedRefImpl 类实现。

示例应用

以下是如何使用上述实现创建响应式对象、引用和计算属性的示例:

const state = reactive({ count: 0 });

effect(() => {
  console.log(`Count is: ${state.count}`);
});

state.count++; // 输出: Count is: 1

const countRef = ref(0);

effect(() => {
  console.log(`CountRef is: ${countRef.value}`);
});

countRef.value++; // 输出: CountRef is: 1

const doubled = computed(() => state.count * 2);

effect(() => {
  console.log(`Doubled count is: ${doubled.value}`);
});

state.count++; // 输出: Count is: 2, Doubled count is: 4

Vue 3 的 reactivity 库通过 Proxy 和依赖收集机制实现了响应式系统。reactive 函数将对象转换为响应式对象,ref 函数将基本类型值包装为响应式对象,computed 函数创建基于其他响应式数据的计算属性。通过这些 API,Vue 3 能够高效地管理和更新视图。