当前位置:首页 > VUE

vue 观察者实现

2026-02-24 06:42:43VUE

Vue 观察者实现原理

Vue 的响应式系统核心基于观察者模式,通过 Object.defineProperty(Vue 2)或 Proxy(Vue 3)实现数据劫持,结合依赖收集和派发更新机制实现数据变化时的自动视图更新。

Vue 2 实现方式

数据劫持 通过 Object.defineProperty 递归遍历对象属性,将属性转为 getter/setter:

function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get() {
      console.log('读取属性', key);
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        console.log('设置属性', key);
        val = newVal;
      }
    }
  });
}

依赖收集 每个被观察的数据属性关联一个 Dep 实例(依赖管理器),在 getter 中收集依赖(Watcher),在 setter 中通知更新:

class Dep {
  constructor() {
    this.subs = []; // 存储 Watcher 实例
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

function defineReactive(obj, key, val) {
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 当前 Watcher 实例
        dep.addSub(Dep.target);
      }
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        val = newVal;
        dep.notify(); // 触发更新
      }
    }
  });
}

Watcher 实现 Watcher 作为观察者,在初始化时触发 getter 以收集依赖:

class Watcher {
  constructor(vm, exp, cb) {
    this.vm = vm;
    this.exp = exp;
    this.cb = cb;
    Dep.target = this; // 标记当前 Watcher
    this.value = this.get(); // 触发 getter 收集依赖
    Dep.target = null;
  }
  get() {
    return this.vm[this.exp]; // 触发数据属性的 getter
  }
  update() {
    const newValue = this.get();
    if (newValue !== this.value) {
      this.cb(newValue);
      this.value = newValue;
    }
  }
}

Vue 3 实现方式

Vue 3 改用 Proxy 实现数据劫持,解决 Vue 2 中无法检测新增属性/数组变化的问题:

vue 观察者实现

Proxy 代理

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

依赖管理

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());
}

Effect 函数 替代 Vue 2 的 Watcher,通过 effect 函数自动执行依赖收集:

vue 观察者实现

let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn(); // 执行时触发 getter 收集依赖
  activeEffect = null;
}

关键差异

  1. 劫持方式

    • Vue 2 使用 Object.defineProperty,需递归遍历对象且无法检测新增属性。
    • Vue 3 使用 Proxy,支持动态属性添加和数组索引修改。
  2. 性能优化

    • Vue 3 的 Proxy 直接代理整个对象,无需递归初始化所有属性。
  3. 依赖追踪

    • Vue 2 通过 Dep/Watcher 层级结构管理依赖。
    • Vue 3 通过 WeakMap/Map/Set 实现更细粒度的依赖关系。

标签: 观察者vue
分享给朋友:

相关文章

vue表格实现教学

vue表格实现教学

Vue 表格实现方法 使用原生HTML表格 在Vue模板中直接使用HTML的<table>标签,结合v-for动态渲染数据: <template> <table&g…

vue路由实现内部切换

vue路由实现内部切换

Vue 路由实现内部切换的方法 Vue Router 是 Vue.js 官方的路由管理器,用于实现单页应用(SPA)的页面切换。以下是几种常见的内部路由切换方式: 使用 <router-lin…

vue实现旋转

vue实现旋转

Vue 实现旋转效果的方法 在 Vue 中实现旋转效果可以通过多种方式完成,以下是几种常见的方法: 使用 CSS 动画 通过 Vue 的 :class 或 :style 绑定 CSS 动画实现旋转…

vue 实现tabs

vue 实现tabs

Vue 实现 Tabs 组件的方法 使用动态组件和 v-for 在 Vue 中实现 Tabs 可以通过动态组件和 v-for 指令结合完成。定义一个 tabs 数组,包含每个标签的标题和对应的组件。…

vue 实现级联

vue 实现级联

Vue 实现级联选择器 级联选择器(Cascader)常用于省市区选择、分类选择等场景。Vue 中可以通过 Element UI、Ant Design Vue 等 UI 库实现,也可以手动封装。…

vue实现granfana

vue实现granfana

Vue 实现类似 Grafana 的仪表盘 要在 Vue 中实现类似 Grafana 的仪表盘功能,需要结合数据可视化库、状态管理和 UI 组件。以下是关键步骤和推荐工具: 数据可视化库 使用…