Vue响应式系统演进与实现解析

发布于:2025-05-21 ⋅ 阅读:(23) ⋅ 点赞:(0)
一、Vue 2 响应式实现详解

1. 核心代码实现

// 依赖收集器(观察者模式)
class Dep {
  constructor() {
    this.subscribers = new Set();
  }
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }
  notify() {
    this.subscribers.forEach(effect => effect());
  }
}

let activeEffect = null;

// 响应式转换核心
function defineReactive(obj, key, val) {
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      dep.depend(); // 收集依赖
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        val = newVal;
        dep.notify(); // 触发更新
      }
    }
  });
}

// 递归处理对象
function observe(obj) {
  Object.keys(obj).forEach(key => {
    let value = obj[key];
    if (typeof value === 'object' && value !== null) {
      observe(value); // 递归处理嵌套对象
    }
    defineReactive(obj, key, value);
  });
}

// 数组方法重写
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift'].forEach(method => {
  const original = arrayProto[method];
  arrayMethods[method] = function(...args) {
    const result = original.apply(this, args);
    this.__ob__.dep.notify(); // 手动触发更新
    return result;
  };
});

2. 使用步骤

const data = { count: 0, list: [1,2,3] };
observe(data);

// 定义响应式副作用
function watchEffect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

watchEffect(() => {
  console.log('Count changed:', data.count);
});

data.count = 5; // 触发日志输出
data.list.push(4); // 触发更新

3. 关键问题示例

// 动态属性问题
data.newProp = 'test'; // ❌ 不会触发更新
Vue.set(data, 'newProp', 'test'); // ✅ 正确方式

// 数组索引修改问题
data.list[0] = 99; // ❌ 不会触发
data.list.splice(0, 1, 99); // ✅ 正确方式

二、Vue 3 响应式实现详解

1. 核心代码实现

const targetMap = new WeakMap(); // 存储对象-键-依赖关系

function track(target, key) {
  if (!activeEffect) 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()));
  }
  dep.add(activeEffect);
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const effects = depsMap.get(key);
  effects && effects.forEach(effect => effect());
}

const reactiveMap = new WeakMap();

function reactive(obj) {
  if (reactiveMap.has(obj)) return reactiveMap.get(obj);
  
  const proxy = new Proxy(obj, {
    get(target, key, receiver) {
      track(target, key);
      const res = Reflect.get(target, key, receiver);
      if (typeof res === 'object' && res !== null) {
        return reactive(res); // 惰性递归代理
      }
      return res;
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(target, key);
      return true;
    },
    // 处理 delete 操作
    deleteProperty(target, key) {
      const hasKey = Object.prototype.hasOwnProperty.call(target, key);
      const result = Reflect.deleteProperty(target, key);
      if (hasKey && result) {
        trigger(target, key);
      }
      return result;
    }
  });
  
  reactiveMap.set(obj, proxy);
  return proxy;
}

// 处理基本类型
function ref(value) {
  return {
    get value() {
      track(this, 'value');
      return value;
    },
    set value(newVal) {
      value = newVal;
      trigger(this, 'value');
    }
  };
}

2. 使用步骤

const state = reactive({ count: 0, items: ['apple'] });
const num = ref(10);

// 响应式副作用
function effect(fn) {
  activeEffect = fn;
  fn();
  activeEffect = null;
}

effect(() => {
  console.log('State changed:', state.count, num.value);
});

state.count++; // ✅ 触发更新
state.items.push('banana'); // ✅ 自动触发
num.value = 20; // ✅ 触发更新

三、技术对比全景图
特性 Vue 2 (Object.defineProperty) Vue 3 (Proxy)
动态属性检测 需手动调用 Vue.set 自动检测
数组处理 需重写 7 个方法 原生支持所有操作
性能初始化 O(n) 立即递归 O(1) 惰性代理
嵌套对象处理 初始化时完全递归 按需代理
数据类型支持 对象/数组 支持 Map/Set/WeakMap 等
兼容性 IE9+ 不支持 IE11 及以下
内存占用 每个属性创建 Dep 实例 整个对象共享依赖映射

四、关键演进技术点

1. 依赖收集机制升级

  • Vue 2:每个属性对应一个 Dep 实例
  • Vue 3:通过 WeakMap 建立三级映射关系:
    targetMap: WeakMap<Target, depsMap>
    depsMap: Map<Key, dep>
    dep: Set<Effect>
    

2. 性能优化策略

  • 惰性代理:只有访问到的属性才会被代理
  • 缓存机制reactiveMap 避免重复代理同一对象
  • 批量更新:通过调度器合并多次数据变更

3. 新数据结构支持

// 支持 Map 的响应式
const mapState = reactive(new Map());
mapState.set('key', 'value'); // ✅ 自动触发更新

// 支持 Set 的操作
const setState = reactive(new Set());
setState.add(123); // ✅ 触发更新

五、最佳实践指南

Vue 2 项目注意事项

  1. 动态添加属性必须使用 Vue.set
  2. 数组操作优先使用变异方法
  3. 复杂数据结构建议提前初始化完整结构

Vue 3 开发技巧

// 解构保持响应式
const state = reactive({ x: 1, y: 2 });
const { x, y } = toRefs(state); // 使用 toRefs

// 组合式 API 示例
import { reactive, watchEffect } from 'vue';

export default {
  setup() {
    const state = reactive({ count: 0 });
    
    watchEffect(() => {
      console.log('Current count:', state.count);
    });

    return { state };
  }
}

六、总结与展望

核心演进价值

  1. 开发体验:减少心智负担,代码更符合直觉
  2. 性能突破:大型应用响应式处理效率提升 2-5 倍
  3. 扩展能力:为未来响应式数据库等高级特性铺路

通过深入理解 Vue 响应式系统的演进,开发者不仅能写出更高效的代码,更能把握前端框架设计的核心思想。这种从「属性劫持」到「代理拦截」的转变,体现了前端技术追求更优雅、更高效的永恒主题。


网站公告

今日签到

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