坦途的博客

vuePress-theme-reco 坦途    2020 - 2025
坦途的博客 坦途的博客
主页
博客
  • 开发实践
  • 技术笔记
  • 技术实践
  • 前端优化
  • 鉴权
  • 心情物语
  • 杂谈游记
  • 前端
标签
时间轴
关于
author-avatar

坦途

22

文章

33

标签

主页
博客
  • 开发实践
  • 技术笔记
  • 技术实践
  • 前端优化
  • 鉴权
  • 心情物语
  • 杂谈游记
  • 前端
标签
时间轴
关于

Vue3 响应式系统的底层原理解析

vuePress-theme-reco 坦途    2020 - 2025

Vue3 响应式系统的底层原理解析

坦途 2024-11-02 vue

摘要

Vue3 的响应式系统使用 Proxy 替代了 Vue2 的 Object.defineProperty,大大提升了性能和灵活性,同时提供了 reactive 和 ref 这两种方式来创建响应式数据。

# 一、Vue2 的响应式系统问题

在 Vue2 中,Vue 采用 Object.defineProperty 来拦截对象属性的读写操作,以实现数据响应式。

# Vue2 的数据劫持方式

let data = {};
Object.defineProperty(data, 'message', {
  get() {
    console.log('获取 message 值');
    return 'Hello Vue2';
  },
  set(newVal) {
    console.log('设置 message 值:', newVal);
  }
});

data.message; // 触发 get
data.message = 'Vue2 变更'; // 触发 set

# Vue2 的问题

  1. 无法检测新加的属性
    Vue2 只会在 初始化 时劫持已有属性,如果动态添加新属性,Vue 不会 监听。

    let obj = {};
    Vue.set(obj, 'count', 1); // 需要 Vue.set 才能让新属性变成响应式
    
  2. 数组的变更监听存在缺陷
    只能监听 push、pop 等方法,而不能监听索引变更,如:

    let arr = [1, 2, 3];
    arr[0] = 100; // 视图不会更新!
    
  3. 深层嵌套对象的性能问题
    Vue2 需要递归遍历整个对象,在初始化时 给每个属性 添加 getter 和 setter,对 大对象性能影响较大。


# 二、Vue3 如何优化响应式

Vue3 使用 Proxy 代理整个对象,而不是逐个属性劫持,解决了 Vue2 的所有问题。

# Vue3 Proxy 实现响应式

let data = new Proxy({ message: 'Hello Vue3' }, {
  get(target, key) {
    console.log(`读取 ${key}`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`设置 ${key} 为 ${value}`);
    target[key] = value;
    return true;
  }
});

data.message; // 触发 get
data.message = 'Vue3 变更'; // 触发 set

# Vue3 Proxy 的优势

✅ 支持动态新增属性

let obj = reactive({});
obj.count = 1; // 直接赋值就能变成响应式

✅ 正确监听数组变化

let arr = reactive([1, 2, 3]);
arr[0] = 100; // 视图会更新!

✅ 性能优化 Vue3 不再需要递归遍历所有属性,而是当访问某个属性时才进行劫持,提升初始化性能。


# 三、Vue3 响应式 API

Vue3 提供了两种响应式 API:

1️⃣ reactive:适用于对象和数组

import { reactive } from 'vue';

const state = reactive({
  count: 0,
  info: { name: 'Vue3' }
});

state.count++; // 响应式更新
state.info.name = 'Vue3 Updated'; // 深层嵌套仍是响应式

2️⃣ ref:适用于基本数据类型

import { ref } from 'vue';

const count = ref(0);
count.value++; // 需要 .value 才能访问

✅ reactive 适用于对象,✅ ref 适用于基础类型,组合使用能实现更高效的状态管理。


# 四、Vue3 响应式在实际开发中的应用

Vue3 的响应式系统不仅提高了性能,还让代码更加简洁。在实际开发中,我们可以利用 Vue3 响应式 API 来实现 数据驱动 UI。

示例:计数器

<template>
  <div>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);
const increment = () => count.value++;
</script>

✅ 代码更简单:没有 this,也不需要 data() 和 methods,逻辑更直观!


# 结论

版本 响应式方式 监听对象新增属性 监听数组索引 初始化性能
Vue2 Object.defineProperty ❌ 需要 Vue.set ❌ 不支持 ❌ 递归所有属性
Vue3 Proxy ✅ 支持 ✅ 支持 ✅ 访问时才劫持

Vue3 通过 Proxy 重构了响应式系统,提供了 reactive 和 ref API,解决了 Vue2 的局限性,使得数据管理更简单,推荐在新项目中优先使用。