侧边栏壁纸
  • 累计撰写 47 篇文章
  • 累计创建 2 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Vue:Vue2和Vue3的响应式实现原理

Vue 的响应式系统是其核心特性之一,主要依赖数据劫持结合发布-订阅模式来实现数据变化时视图的自动更新

Vue 2:基于 Object.defineProperty
  1. 数据劫持(响应式化)
    Vue 在初始化时会遍历 data 中的属性,使用 Object.defineProperty 将它们转换为 getter/setter,从而拦截对数据的读取和修改。
  2. 依赖收集(Dependency Collection)
    在 getter 中,会将当前的 Watcher(依赖)添加到 Dep(依赖管理器)中。
    每个响应式属性都有一个对应的 Dep 实例,用于存储所有依赖于该属性的 Watcher。
  3. 派发更新(Dependency Notification)
    当数据被修改时,setter 会被触发,并通知 Dep 中所有的 Watcher 进行更新。
    Watcher 会执行其回调(例如更新视图、计算属性等),完成响应式更新。
  4. 数组的处理
    Vue 2 对数组的方法(如 push、pop、splice 等)进行了重写,使得通过这些方法修改数组也能触发视图更新。
// 简化版响应式实现
class Dep {
  constructor() {
    this.subscribers = new Set();
  }
  
  depend() {
    if (activeWatcher) {
      this.subscribers.add(activeWatcher);
    }
  }
  
  notify() {
    this.subscribers.forEach(watcher => watcher.update());
  }
}

let activeWatcher = null;

class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn;
    this.update();
  }
  
  update() {
    activeWatcher = this;
    this.updateFn();
    activeWatcher = null;
  }
}

function observe(data) {
  if (typeof data !== 'object' || data === null) return;
  
  Object.keys(data).forEach(key => {
    let value = data[key];
    const dep = new Dep();
    
    Object.defineProperty(data, key, {
      get() {
        dep.depend();
        return value;
      },
      set(newValue) {
        if (newValue !== value) {
          value = newValue;
          dep.notify();
        }
      }
    });
    
    observe(value);
  });
}

// 使用示例
const data = { count: 0, name: 'Vue' };
observe(data);

new Watcher(() => {
  console.log(`Count changed: ${data.count}`);
});

data.count = 1; // 输出: Count changed: 1

局限:
无法检测到对象属性的添加和删除(需用 Vue.set/Vue.delete)
数组变异方法需重写(push、pop 等),通过拦截器实现
初始化时递归遍历整个对象,性能有损耗

Vue 3:基于 Proxy
  1. 更强大的拦截
    Proxy 可以直接拦截整个对象,包括属性的增删、数组索引修改、length 变化等,无需递归遍历所有属性。
  2. 响应式核心
    reactive():返回一个对象的 Proxy 代理,拦截所有操作。
    ref():将基本类型包装为 { value: ... }对象,并通过 getter/setter 实现响应式。
  3. 依赖收集与触发
    通过 track()在 get 操作时收集依赖(存储到全局的 targetMap)。
    通过 trigger()在 set 操作时触发更新。
// 简化版 Proxy 实现
let activeEffect = null;

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

const targetMap = new WeakMap();

function track(target, key) {
  if (!activeEffect) return;
  
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Set();
    depsMap.set(key, dep);
  }
  
  dep.add(activeEffect);
}

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

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key);
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const result = Reflect.set(target, key, value);
      trigger(target, key);
      return result;
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      trigger(target, key);
      return result;
    }
  });
}

// 使用示例
const state = reactive({ count: 0, name: 'Vue3' });

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

state.count = 1; // 输出: Count: 1
state.newProp = '动态添加'; // 也能触发响应式

0

评论区