Vue 响应式原理揭秘:数据是如何自动更新视图的

Vue 响应式原理揭秘:数据是如何自动更新视图的

在使用 Vue 时,你是否想过:当你修改一个变量,页面是如何自动更新的? 这背后其实就是 Vue 的响应式系统在发挥作用。

本文将深入探讨 Vue 的响应式原理,包括其核心机制、关键实现代码解析(基于 Vue 2 和 Vue 3 的不同实现),并通过简单示例来帮助你更好地理解“数据驱动视图”这一核心理念。


一、什么是响应式系统?

响应式系统(Reactivity System)指的是:当数据发生变化时,视图能自动感知并更新,无需开发者手动操作 DOM。

例如:

<div id="app">
  <p>{{ message }}p>
div>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
script>

当我们执行 vm.message = '你好 Vue!',页面中的

内容会自动更新,这就是响应式的效果。


二、Vue 2 响应式原理解析(基于 Object.defineProperty)

2.1 核心概念

Vue 2 的响应式系统依赖以下几个核心概念:

  • Observer:将对象变成响应式的。
  • Dep(依赖管理器):收集依赖(watcher)。
  • Watcher:订阅者,视图的更新者。

2.2 响应式的建立过程

当我们在 data 中定义属性时,Vue 会递归遍历每个属性,使用 Object.defineProperty 将其转换为 getter 和 setter:

function defineReactive(obj, key, val) {
  const dep = new Dep(); // 依赖收集器

  Object.defineProperty(obj, key, {
    get() {
      Dep.target && dep.addSub(Dep.target); // 收集依赖
      return val;
    },
    set(newVal) {
      if (val !== newVal) {
        val = newVal;
        dep.notify(); // 通知依赖更新
      }
    }
  });
}

2.3 依赖收集与更新

class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(watcher) {
    this.subs.push(watcher);
  }
  notify() {
    this.subs.forEach(watcher => watcher.update());
  }
}

Watcher 的角色是“订阅数据变化”,一旦数据发生变化,它就会执行 update() 来更新视图。


三、Vue 3 响应式原理解析(基于 Proxy)

Vue 3 使用了现代浏览器支持的 Proxy 替代 Object.defineProperty,实现了更高效和灵活的响应式系统。

3.1 基本用法

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

watchEffect(() => {
  console.log(`count is: ${state.count}`);
});
state.count++; // 自动触发响应

3.2 Proxy 实现核心逻辑

function reactive(target) {
  const handler = {
    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;
    }
  };
  return new Proxy(target, handler);
}

相比 Vue 2 的 defineProperty,Proxy 支持数组、嵌套对象、属性新增等更多情况。


四、响应式系统中的依赖追踪与副作用函数

Vue 3 中依赖收集与触发由 effecttrack/trigger 实现:

let activeEffect;

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

const bucket = new WeakMap();

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

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

这是响应式系统的最小实现模型,非常有助于深入理解。


五、对比 Vue 2 和 Vue 3 的响应式原理

特性 Vue 2 (defineProperty) Vue 3 (Proxy)
基础 API Object.defineProperty Proxy
支持新增属性 ❌ 需使用 Vue.set ✅ 原生支持
数组支持 ❌ 局限较多 ✅ 全面支持
性能 一定递归开销 更高效
实现难度 较复杂,遍历对象属性 更简洁

六、实战:手写一个简单的响应式系统

function reactive(obj) {
  const depsMap = new Map();

  return new Proxy(obj, {
    get(target, key) {
      if (!depsMap.has(key)) depsMap.set(key, new Set());
      depsMap.get(key).add(activeEffect);
      return target[key];
    },
    set(target, key, value) {
      target[key] = value;
      if (depsMap.has(key)) {
        depsMap.get(key).forEach(fn => fn());
      }
      return true;
    }
  });
}

let activeEffect = null;

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

// 使用
const state = reactive({ count: 0 });
effect(() => {
  console.log(`count: ${state.count}`);
});
state.count++;

七、总结

Vue 的响应式系统是其核心优势之一,真正实现了数据驱动视图。

  • Vue 2 使用 Object.defineProperty,存在一些局限性;
  • Vue 3 使用 Proxy,支持更多功能和更高性能;
  • 响应式系统的核心在于:依赖收集、变更检测、通知更新 三个阶段。

你可能感兴趣的:(前端,vue.js,前端,javascript)