这篇文章的目标只有一个:让你在任何场景下,都能毫不犹豫地判断:这个地方该用 ref ,还是 reactive
目录
- 响应式的本质不是变量
- [ref 是什么?什么时候该用 ref?](#ref 是什么?什么时候该用 ref?)
-
- [ref 的本质](#ref 的本质)
- [什么时候必须用 ref(重点)](#什么时候必须用 ref(重点))
-
- [场景 1:基本类型](#场景 1:基本类型)
- [场景 2:对象 / 数组会被整体替换](#场景 2:对象 / 数组会被整体替换)
- [场景 3:对象是"状态",而不是"模型"](#场景 3:对象是“状态”,而不是“模型”)
- [ref 的优缺点](#ref 的优缺点)
- [reactive 是什么?什么时候该用 reactive?](#reactive 是什么?什么时候该用 reactive?)
-
- [reactive 的本质](#reactive 的本质)
- [什么时候 reactive 是最优解](#什么时候 reactive 是最优解)
-
- [场景 1:表单对象](#场景 1:表单对象)
- [场景 2:长期存在的业务模型](#场景 2:长期存在的业务模型)
- [reactive 的优缺点](#reactive 的优缺点)
- [对象到底用 ref 还是 reactive?](#对象到底用 ref 还是 reactive?)
- 响应式是怎么"失去"的
-
- [reactive 失效的三大原因](#reactive 失效的三大原因)
- [ref 失效的常见原因](#ref 失效的常见原因)
- [toRef / toRefs:解决解构问题](#toRef / toRefs:解决解构问题)
-
- [toRef 是什么](#toRef 是什么)
- [toRefs 批量使用](#toRefs 批量使用)
- 总结
响应式的本质不是变量
在 Vue3 里,响应式的本质不是变量,而是"引用关系"
- ref :用一个对象包住一个值,通过 .value 访问
- reactive :用 Proxy 代理一个对象
⚠️:Vue 追踪的不是变量名,而是 ref.value 或 Proxy 内部的属性访问
ref 是什么?什么时候该用 ref?
ref 的本质
typescript
const count = ref(0)
- count 是一个对象
- 真正的值在 count.value
- Vue 通过 getter / setter 追踪 .value
在模板里:
typescript
{{ count }} // 自动解包
在js/ts中:
typescript
count.value++
什么时候必须用 ref(重点)
场景 1:基本类型
typescript
const loading = ref(false)
const page = ref(1)
const keyword = ref('')
场景 2:对象 / 数组会被整体替换
typescript
const list = ref<Item[]>([])
list.value = res.data
如果用 reactive:
typescript
const list = reactive([])
list = res.data // ❌ 直接失效
只要你会写 = 重新赋值,就用 ref!!!
场景 3:对象是"状态",而不是"模型"
typescript
const currentRow = ref<Row | null>(null)
- 有 / 没有
- 选中 / 取消
- 打开 / 关闭
这是状态,不是业务结构
ref 的优缺点
优点:
- 可以整体替换
- 可以为 null / undefined
- 解构安全
- 状态语义清晰
缺点:
- JS 中需要 .value
- 对象层级深时略显啰嗦
reactive 是什么?什么时候该用 reactive?
reactive 的本质
typescript
const form = reactive({ name: '', age: 18 })
- 返回的是一个 Proxy
- 每个属性访问都会被 Vue 追踪
- 没有 .value
什么时候 reactive 是最优解
场景 1:表单对象
typescript
const form = reactive({
name: '',
age: 0,
role: ''
})
原因:
- 字段多
- 高频修改单字段
- v-model 非常频繁
- 不需要整体替换
场景 2:长期存在的业务模型
typescript
const state = reactive({
loading: false,
page: 1,
pageSize: 10
})
这些数据:
- 生命周期一致
- 逻辑上属于一组
- 不会被整体重置
reactive 的优缺点
优点:
- 写法自然
- 表单 / v-model 体验极佳
- 代码可读性高
缺点:
- ❌ 不能整体替换
- ❌ 不能为 null
- ❌ 解构会丢响应式
对象到底用 ref 还是 reactive?
四个问题法(直接用)
| ref | reactive | |
|---|---|---|
| 会不会被整体替换 | 会 -> ref | 不会 -> reactive |
| 关心"对象是谁"or"字段怎么变"? | 是谁 -> ref | 字段 -> reactive |
| 会不会大量 v-model | 会 -> reactive | |
| 是不是长期业务模型 | 是 -> reactive |
响应式是怎么"失去"的
reactive 失效的三大原因
typescript
form = newForm // ❌ 整体替换
const { name } = form // ❌ 解构
JSON.parse(JSON.stringify(form)) // ❌ 深拷贝
正确做法:
typescript
Object.assign(form, newForm)
ref 失效的常见原因
typescript
const c = count.value // ❌ 断开引用
因为这里只是把count这个响应式的值给了c而已
toRef / toRefs:解决解构问题
toRef 是什么
typescript
const name = toRef(form, 'name')
- 把 reactive 的某个字段变成 ref
- 双向同步
toRefs 批量使用
typescript
const { name, age } = toRefs(form)
它们的原理都是 把reactive 内的变量都解构成响应式的ref包裹的变量
总结
- reactive 负责"结构"
- ref 负责"状态"