主题
概念卡片:Vue3 响应式原理
一句话机制:Vue3 用 Proxy 代理对象实现响应式——
ref/reactive把数据包成响应式引用,模板或watch读取时「收集依赖」,数据变化时 Proxy 的 set 陷阱「触发更新」。响应式体系除了定义数据的ref/reactive,还有**派生数据的computed(缓存)和监听变化的watch(副作用)**两类配套。
四个数据 API 的分工
| API | 作用 | 访问/修改方式 |
|---|---|---|
ref(x) | 基本类型 → 响应式引用 | .value(模板自动解包) |
reactive(obj) | 对象/数组 → 响应式代理 | 直接 .属性 |
toRef(obj,'k') | 抽单属性、保持响应式 | .value |
toRefs(obj) | 抽全部属性、保持响应式 | 各属性 .value |
关键代码示例
ref(基本类型,靠 .value 读写):
js
import { ref } from 'vue'
const message = ref('Hello World')
setTimeout(() => { message.value = '王源' }, 2000) // 2 秒后视图自动更新reactive + toRefs(对象,解构不丢响应式):
js
import { reactive, toRefs } from 'vue'
const dog = reactive({ id: 1, name: '哈士奇', age: 2 })
const { name, age } = toRefs(dog) // 解构后仍是响应式
setTimeout(() => { name.value = '泰迪' }, 3000)注意:
toRef/toRefs的前提是源对象本身已是响应式,否则抽出来的引用不响应。
computed(缓存派生值)与 watch(监听副作用)
js
import { ref, computed, watch } from 'vue'
const message = ref('Hello World')
// computed:依赖变化才重算,结果缓存
const reversedMessage = computed(() => message.value.split('').reverse().join(''))
// watch:数据变化后执行副作用(发请求、操作 DOM…)
watch(message, (newVal, oldVal) => {
console.log('变了:', oldVal, '->', newVal)
})watch 的三个参数与两个关键选项:
js
watch(来源, (newVal, oldVal) => {}, { deep, immediate })| 选项 | 作用 | 默认 |
|---|---|---|
deep | 深度监听对象内部属性变化(对象默认监听不到内部属性) | false |
immediate | 初次加载立即执行一次回调 | false |
watch的来源可以是:ref、reactive 对象、getter 函数() => user.address.city,或它们的数组。
完整对比
ref vs reactive:
| ref | reactive | |
|---|---|---|
| 适用 | 基本类型(也能包对象) | 仅对象/数组 |
| 读写 | .value | 直接 .属性 |
| 解构 | 单值无此问题 | 解构丢响应式,需 toRefs |
computed vs watch:
| computed | watch | |
|---|---|---|
| 用途 | 派生一个新值(有返回值) | 监听变化执行副作用 |
| 缓存 | 有(依赖不变不重算) | 无 |
| 典型场景 | 反转、过滤、合计 | 请求、防抖、DOM 操作 |
不变量(必须成立的约束)
ref在<script setup>模板里自动解包,普通<script>里必须.value。- 解构
reactive会丢响应式——必须toRefs解构。 computed只有依赖的数据变化才重算,否则读缓存。watch监听对象内部属性默认监听不到,要设deep: true。- Proxy 能拦截新增/删除属性和数组索引——Vue2
defineProperty做不到,这是 Vue3 响应式的根本升级。
踩坑案例
- 现象:
const { name } = reactive({name:'哈士奇'})解构后改name视图不更新。 原因:解构拿到的是普通值快照,已切断 Proxy 代理。解决:const { name } = toRefs(dog)。 - 现象:
watch(obj, cb)改了obj.inner却无回调。 原因:watch 默认浅监听,对象内部属性变化监听不到。解决:watch(obj, cb, { deep: true })。
常见误解
- 以为
ref只能存基本类型 → 也能存对象,内部自动转reactive。 - 以为
computed每次访问都重算 → 有缓存,依赖不变就返回缓存值。 - 忘了
.value:setup内、watch回调里直接给ref赋值,改了不生效。
关联
- 总览:Vue3技术栈总览
- 生命周期:概念卡片:Vue3生命周期
- 源:
B40-资源/语雀-Leo的知识库/框架与vue3/05_Vue3/04_Vue基础开发/(03_refreactive / 10_computed / 11_watch 3 篇)