• 分类: vue 源码

近期在阅读 vue3 的源码,但源码量巨大且包含了各种边界处理、特殊框架需求等,使得理解起来很难;想了下,从各个模块主线入手,拆解出核心功能,逐个梳理下,从简单到复杂的实现基本的 vue3 框架;达到理解的目的;

以下,均从最简单的一个{num:0}对象开始,针对其实现响应式;

一. 最简单的实现

设定如下的场景,单击 add 按钮后,counter.num值变化,触发 effect 内匿名函数执行,达到响应式效果;

<button onclick="add()">add</button>
<div id="value"></div>
<script>
  // 1.获取响应式对象
  const counter = reactive({ num: 0 });
  // 2.副作用函数
  effect(() => {
    console.log("effect", counter);
    document.getElementById("value").innerText = counter.num;
  });
  // 3.触发
  let i = 1;
  window.add = () => {
    counter.num = i++;
  };
</script>

在线演示

See the Pen vue3- by 唐鸽 (@tggcs) on CodePen.

对于源码摘录了主要的部分,实现如上功能,分为 3 部分;

1. 定义全局的当前副作用函数,依赖收集桶;
// 全局变量
const targetMap = new WeakMap();
let activeEffect;
2. 实现依赖收集、触发,即`track`,`trigger`;

vue3 用 proxy 代理,最基本的就是在 get,set 内设置收集和触发的机制;

// 实现响应式对象,拦截get,set,加入track,trigger
function track(target, type, key) {
  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()));
  }

  dep.add(activeEffect);

  console.log("track", targetMap);
}

function trigger(target, type, key, newValue) {
  const depsMap = targetMap.get(target);

  console.log("\n trigger", target, type, key, newValue);
  depsMap.get(key).forEach((effect) => {
    effect.run();
  });
}

function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver);
      track(target, "get", key);
      return res;
    },
    set(target, key, value, receiver) {
      const res = Reflect.set(target, key, value, receiver);
      trigger(target, "set", key, value);
      return res;
    },
  });
}
3. 实现副作用函数;
class ReactiveEffect {
  fn;
  constructor(fn) {
    this.fn = fn;
  }
  run() {
    console.log("run", this);
    activeEffect = this;
    return this.fn();
  }
}

function effect(fn) {
  const _effect = new ReactiveEffect(fn);
  _effect.run();
}

bucket 桶结构,收集副作用函数

二. 拆分文件

github 地址

演示地址

- .
  ├── test.html # 入口
  ├── src  
  │ ├── baseHandlers.js # proxy 代理实现
  │ ├── effect.js # 依赖收集&触发逻辑
  │ ├── operations.js # 枚举
  │ ├── reactive.js # 响应式封装