javascript

Vue 3 响应式:ref、reactive 和 toRefs 怎么选

围绕基本类型、对象、解构丢失响应性这三个高频问题,解释 Vue 3 里什么时候用 ref,什么时候用 reactive,以及 toRefs 到底解决什么。

·更新于 ·阅读约 8 分钟·计算中...
Vue 3 响应式:ref、reactive 和 toRefs 怎么选

Vue 3 响应式最常见的困惑,不是“Proxy 原理是什么”,而是三件更实际的事:

  • 一个普通值为什么要包进 ref
  • 一个对象为什么有时用 reactive
  • 为什么对象一解构就“没反应了”

如果先把这三个问题理顺,refreactivetoRefs 的选择就会清楚很多。

什么是响应式

在 Vue 3 中,响应式 是指 Vue 对数据进行“代理”,使得数据的变化能够被追踪,并且当数据变化时,相关的视图部分会自动更新。Vue 使用现代的 JavaScript Proxy API 来实现这一点,能够拦截对象属性的读取和修改。

基本类型和引用类型为什么影响写法

基本类型

基本类型包括:StringNumberBooleanNullUndefinedSymbol。这些数据类型的特点是 值传递。当将一个基本类型的变量赋值给另一个变量时,新的变量会持有该值的副本。修改副本不会影响原始值。

let num1 = 10;
let num2 = num1;  // num2 是 num1 的副本
num2 = 20;         // 修改 num2 不会影响 num1
console.log(num1);  // 输出 10

在 Vue 3 里,基本类型通常用 ref 包起来,因为 Vue 需要一个可以被追踪的壳:

import { ref } from 'vue';

const count = ref(0);
count.value = 1;  // 修改基本类型变量

引用类型

引用类型包括:ObjectArrayFunctionDateRegExp 等。这些数据类型的特点是 引用传递。当将一个引用类型的变量赋值给另一个变量时,两个变量指向同一内存地址,因此修改其中一个变量的属性或内容会直接影响到另一个变量。

let obj1 = { name: 'Vue3' };
let obj2 = obj1;   // obj2 和 obj1 引用同一个对象
obj2.name = 'Vue 3';  // 修改 obj2 会影响 obj1
console.log(obj1.name);  // 输出 'Vue 3'

对象更适合 reactive,因为它能直接把对象代理成可追踪结构:

import { reactive } from 'vue';

const state = reactive({ count: 0, name: 'Vue3' });
state.count = 1;  // 修改对象的属性,视图会自动更新

refreactive 到底怎么选

1. 响应式深度

  • 基本类型 的响应式是简单的,直接对值本身进行追踪和更新。你可以使用 ref 来处理基本类型数据。
  • 引用类型 的响应式会递归地处理对象内部的属性,使得对象的每个属性都能被 Vue 的响应式系统追踪。这意味着修改嵌套对象的属性也能触发视图的更新。
const state = reactive({ user: { name: 'Vue3' } });
state.user.name = 'Vue 3';  // 修改嵌套属性,视图会自动更新

对于引用类型的嵌套结构,Vue 会深度地追踪每个属性,确保嵌套对象的属性变化能被捕捉到并更新视图。

2. 解构为什么会丢响应性

  • 基本类型的 ref:你拿的是一个 ref 包装对象,本身没有“把内部值拆成普通变量后仍自动响应”这回事。

    const count = ref(0);
    const { value } = count;  // 解构出来的 value 依然是响应式的
    
  • reactive 对象的解构:直接解构后,变量通常不再保持响应式绑定,除非使用 toRefstoRef

    const state = reactive({ count: 0, name: 'Vue3' });
    const { count, name } = state;  // count 和 name 不是响应式的
    
    // 使用 toRefs 保持解构后的变量响应式
    const { count, name } = toRefs(state);  // count 和 name 都是响应式的
    

3. 经验法则

  • 单个值、布尔开关、计数器:ref
  • 表单对象、组件状态对象:reactive
  • 要把 reactive 对象拆出去给模板或组合式函数:toRefs

toRefs 到底解决什么

toRefs 不是“让一切都变高级”,它只是把 reactive 对象的每个属性,转换成仍然和原对象保持连接的 ref:

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

这在组合式 API 里特别常见,因为你经常需要把状态拆给不同函数或模板使用。

常见误区

  • reactive 包一个单独数字,然后到处怀疑为什么不顺手。
  • 直接解构 reactive 后,又以为 Vue “失去响应性”。
  • 为了统一写法,把所有东西都包成 ref,结果对象访问全是 .value.xxx

总结

  • 单值优先 ref,对象优先 reactive
  • 直接解构 reactive 容易丢掉响应性,用 toRefs 保持绑定。
  • 选择 API 的关键不是背定义,而是看数据结构和后续使用方式。

如果你也在比较 React 的状态与副作用边界,可以顺着看 理解与掌控副作用:Vue 3 和 React;如果你在处理对象拷贝和值引用差异,也可以看 JavaScript 对象复制指南

12 分钟

Next.js Image 指南:sizes、fill 和远程图片

基于当前 Next.js Image 官方文档,解释 width/height、fill、sizes、preload、placeholder、remotePatterns 的作用,以及哪些旧教程里的 layout、objectFit 写法已经不该继续照搬。

18 分钟

理解与掌控副作用:前端开发(Vue 3 和 React)中的关键环节

在现代前端开发中,副作用(Side Effects)是一个经常被提到的概念。它指的是那些在执行计算或操作时,不仅影响当前函数的输出,还可能对外部世界产生某些影响的操作。常见的副作用包括:数据请求、事件监听、定时器、DOM 操作等。处理副作用是前端开发中非常重要的一部分,尤其是在构

13 分钟

深入理解 DOM 和 CSSOM:网页渲染的核心

DOM 是浏览器用于表示网页内容和结构的对象模型。它将网页的 HTML 或 XML 文档表示为一个树形结构,每个部分(如元素、文本和属性)都成为一个节点。通过 JavaScript,开发者可以操作这些节点,从而动态地修改页面的内容和结构。CSSOM 是浏览器用来表示 CSS 样式

订阅 FreeMac

每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。