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,是在优化代码,还是在给未来的自己埋坑?**😂

相关推荐
VT.馒头6 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多6 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
-凌凌漆-6 小时前
【vue】pinia中的值使用 v-model绑定出现[object Object]
javascript·vue.js·ecmascript
C澒6 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒6 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式
Charlie_lll7 小时前
学习Three.js–雪花
前端·three.js
onebyte8bits7 小时前
前端国际化(i18n)体系设计与工程化落地
前端·国际化·i18n·工程化
C澒7 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架
BestSongC7 小时前
行人摔倒检测系统 - 前端文档(1)
前端·人工智能·目标检测
0思必得08 小时前
[Web自动化] Selenium处理滚动条
前端·爬虫·python·selenium·自动化