模拟实现Vue2-Vue3响应式更新

发布于:2025-07-14 ⋅ 阅读:(16) ⋅ 点赞:(0)

Vue2作为 MVVM框架

/*
Vue2 通过 Object.defineProperty 监听、挟持数据,实现响应式
并通过 Dep(依赖收集器) 和 Watcher 实现依赖收集,通知视图更新
*/

/*
但是 Vue2用Object.defineProperty 无法监听新增属性、无法监听数组索引变化、无法监听数组长度变化
*/

// 依赖收集器
class Dep {
    constructor() {
        this.subs = [];
    }
    addSub(sub) {
        this.subs.push(sub);
    }
    notify() {
        this.subs.forEach(sub => sub.update());
    }
}

Dep.target = null;

// Observer 数据劫持
// 激活 get,set 函数
function Vue2Reactive(obj, key, val) {
    const dep = new Dep();
    
    Object.defineProperty(obj, key, {
        get() {
            // 依赖收集
            if (Dep.target) {
                dep.addSub(Dep.target);
            }
            return val;
        },
        set(newVal) {
            if (newVal !== val) {
                val = newVal;
                // 通知视图更新
                dep.notify();
            }
        }
    });
}
// 通过 Object.keys(obj) + forEach 遍历对象的所有属性都进行激活
function activeAllProperties(obj) {
    if (typeof obj !== 'object' || obj === null) return;

    Object.keys(obj).forEach(key => {
        Vue2Reactive(obj, key, obj[key]);
    });
}


// Watcher 类
class Watcher {
    constructor(vm, key, callback) {
        this.vm = vm;
        this.key = key;
        this.callback = callback;
        this.value = this.get();
    }
    
    get() {
        Dep.target = this;
        const value = this.vm[this.key];
        Dep.target = null;
        return value;
    }
    
    update() {
        const newValue = this.vm[this.key];
        if (newValue !== this.value) {
            this.value = newValue;
            this.callback(newValue);
        }
    }
}

Vue3

Vue3不再强调 MVVM,其底层是由 TypeScript 编写的,将 Object.defineProperty 换成 Proxy 实现响应式系统,弥补了前者的缺陷,使用编译器将模版换成高效的渲染函数,并通过虚拟 DOM 和调度系统实现高性能更新

/* 为什么要用 Reflect
统一为函数式写法,符合面向对象
报错不会中断,而是返回 false(比如只读的时候)
通过 receiver 参数可以指定 this上下文
*/

const bucket = new WeakMap(); // 用于依赖收集

function Vue3Reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      track(target, key); // 收集依赖
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      trigger(target, key); // 触发更新
      return result;
    }
  });
}

// 收集依赖函数
function track(target, key) {
  if (!activeEffect) return; // 如果没有激活的 effect,直接返回
  let depsMap = bucket.get(target); // 获取 target 的依赖映射
  if (!depsMap) bucket.set(target, (depsMap = new Map()));
  let deps = depsMap.get(key); // 获取 key 的依赖集合
  if (!deps) depsMap.set(key, (deps = new Set())); 
  deps.add(activeEffect); // 将当前激活的 effect 添加到依赖集合
}

// 触发更新函数
function trigger(target, key) {
  const depsMap = bucket.get(target); // 获取 target 的依赖映射
  if (!depsMap) return; // 如果没有依赖,直接返回
  const deps = depsMap.get(key); // 获取 key 的依赖集合
  if (deps) deps.forEach(fn => fn()); // 遍历依赖集合,执行每个依赖的函数
}


// 示例
let activeEffect;
function effect(fn) {
  activeEffect = fn;
  fn(); // 立即执行一次
  activeEffect = null;
}

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

effect(() => {
  console.log('count is:', state.count); // 依赖收集
});

state.count++; // 触发更新

实际上,Vue3在底层上还做了许多细节的优化,比如惰性代理节省操作、微任务批次处理节流更新、自动对依赖进行清理等等


网站公告

今日签到

点亮在社区的每一天
去签到