ref 与 reactive 的本质区别(第3节)

1) ref 的本质:把"一个值"包进响应式盒子里📦

ref() 干的事很直白:

给一个值套个壳,壳里放 .value,Vue 只要看到这个壳的 .value 变了,就会触发依赖更新。

你可以把 ref 想成"带弹簧的盒子":你改盒子里的东西(.value),弹簧就弹一下(触发更新)。

✅ ref 的使用场景

  1. 基础类型(string/number/boolean)
  2. 需要整体替换的对象/数组(比如"直接换一份新数据")
  3. 需要明确以"单个状态"为单位传递/暴露(组合式函数 return 一个状态给外面用)
  4. 和模板配合简单 (模板里会自动帮你解 .value,写起来更顺)

例子:基础类型就老老实实 ref

js 复制代码
import { ref } from 'vue'

const count = ref(0)
const name = ref('Daniel')

function inc() {
  count.value++
}

例子:整体替换对象时 ref 很香

js 复制代码
const user = ref({ id: 1, nick: 'Tom' })

// 整体换掉
user.value = { id: 2, nick: 'Jerry' }

小吐槽:你要是用 reactive,整体替换对象这事就没那么自然(后面会讲)。😅

2) reactive 的本质:把"对象本身"变成一个代理🪞

reactive() 更像是:

给对象套一层 Proxy,拦截 get/set,实现属性级别的依赖追踪与触发更新。

这意味着它天生适合:

  • 结构化状态(多字段、嵌套对象)
  • 频繁改对象的某个属性(而不是整体替换)

✅ reactive 的使用场景

  1. 表单对象(多字段输入、校验、联动)
  2. 业务状态对象(loading/error/data 分组)
  3. 嵌套结构(列表项、复杂配置)

例子:表单最适合 reactive

js 复制代码
import { reactive } from 'vue'

const form = reactive({
  username: '',
  password: '',
  remember: false
})

function reset() {
  form.username = ''
  form.password = ''
  form.remember = false
}

例子:一个请求状态对象

js 复制代码
const state = reactive({
  loading: false,
  error: null,
  data: null
})

async function fetchData() {
  state.loading = true
  state.error = null
  try {
    // ...
    state.data = { ok: true }
  } catch (e) {
    state.error = e
  } finally {
    state.loading = false
  }
}

3) 解构为什么会失去响应式?这坑真是"新手必踩"😭

最经典翻车现场:

js 复制代码
const form = reactive({ username: 'a', password: 'b' })
const { username } = form

username = 'xxx'  // ❌ 改不了 form.username(而且还会报错:Assignment to constant variable)

你可能会说:那我不赋值,我只读呢?

js 复制代码
const { username } = form
console.log(username) // 读到的是一个普通值快照

本质原因:

  • reactive 的响应式能力来自 Proxy 拦截对象属性访问
  • 你一旦 const { username } = form,你取出来的是 当时的值,不是"代理的 getter"
  • 后续 form.username 再变,username 这个变量不会自动跟着变
    👉 这就叫:响应式链路断了

人话:你把"摄像头"拆下来放桌上了,它就不再跟着"监控系统"走了😅

4) toRef / toRefs:专治"解构失灵",给你把链路焊回去🔧

4.1 toRef:把对象的某个属性,变成一个 ref(仍然指向原对象)

js 复制代码
import { reactive, toRef } from 'vue'

const form = reactive({ username: 'a', password: 'b' })
const usernameRef = toRef(form, 'username')

usernameRef.value = 'new'  // ✅ 等价于 form.username = 'new'

你可以理解为:

  • toRef(form, 'username') 创建了一个"带指针的 ref"
  • .value 的读写其实映射到 form.username

适用场景:

✅ 你只想拿其中一个字段出去用(比如子组件只关心 username)

4.2 toRefs:把 reactive 对象的所有属性都转成 ref(方便解构)

js 复制代码
import { reactive, toRefs } from 'vue'

const form = reactive({ username: 'a', password: 'b' })
const { username, password } = toRefs(form)

username.value = 'u'   // ✅ form.username 跟着变
password.value = 'p'   // ✅ form.password 跟着变

适用场景:

✅ 你想解构出来很多字段,还要保持响应式

✅ 用在组合式函数 return 时特别爽(下面会讲选型)

5) 实战选型建议:别纠结,按"状态形态 + 使用方式"选就完事了😄

我给你一套"抄走就能用"的判断规则:

✅ 规则 1:单值、基础类型、开关、计数器 ------ 用 ref

js 复制代码
const visible = ref(false)
const count = ref(0)
const keyword = ref('')

✅ 规则 2:表单、复杂对象、多字段联动 ------ 用 reactive

js 复制代码
const form = reactive({ a: '', b: '', c: 0 })

✅ 规则 3:需要"整体替换"时 ------ ref 更顺手

比如接口返回一整个对象,你经常会这么写:

js 复制代码
const detail = ref(null)
detail.value = await fetchDetail()

✅ 规则 4:组合式函数对外暴露状态 ------ 两种姿势都行,但要统一风格

写法 A:全 ref(return 解构最友好)

js 复制代码
export function useCounter() {
  const count = ref(0)
  const inc = () => count.value++
  return { count, inc }
}

写法 B:reactive + toRefs(既结构化又可解构)

js 复制代码
export function useForm() {
  const form = reactive({ username: '', password: '' })
  const reset = () => { form.username = ''; form.password = '' }
  return { ...toRefs(form), reset }
}

重点:别直接 return form 然后外面解构,不然你就回到"响应式断链"的老路上了😭

✅ 规则 5:模板里想少写 .value ------ reactive 更舒服,但别滥用

模板里 reactive 直接 form.username 读写很自然;
ref 在模板里会自动解包 .value,其实也不麻烦。

真正要避免的是:为了少写 .value,把所有东西都塞 reactive,最后结构巨肥、依赖混乱、调试痛苦😅

最后一句"反问收尾"(拿去当文章结尾钩子😏)

你看,refreactive 从来不是"谁更高级",而是"谁更适合你当前的状态形态"。

所以我反问你一句:

**你现在纠结 ref/reactive,是在优化代码,还是在给未来的自己埋坑?**😂

相关推荐
前端不太难9 小时前
从 Navigation State 反推架构腐化
前端·架构·react
前端程序猿之路10 小时前
Next.js 入门指南 - 从 Vue 角度的理解
前端·vue.js·语言模型·ai编程·入门·next.js·deepseek
大布布将军10 小时前
⚡️ 深入数据之海:SQL 基础与 ORM 的应用
前端·数据库·经验分享·sql·程序人生·面试·改行学it
川贝枇杷膏cbppg10 小时前
Redis 的 RDB 持久化
前端·redis·bootstrap
D_C_tyu10 小时前
Vue3 + Element Plus | el-table 表格获取排序后的数据
javascript·vue.js·elementui
JIngJaneIL10 小时前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
天外天-亮11 小时前
v-if、v-show、display: none、visibility: hidden区别
前端·javascript·html
jump_jump11 小时前
手写一个 Askama 模板压缩工具
前端·性能优化·rust
hellotutu11 小时前
vue2 从 sessionStorage 手动取 token 后,手动加入到 header
vue.js·token·session·header