外观
Vue 响应式原理与 ref
- 状态:未准备
- 适用方向:前端 / Go 全栈
- 最近复习:待填写
- 下次复习:待安排
一句话回答
Vue 3 在响应式读取时追踪依赖、修改时触发相关更新;reactive 主要代理对象,ref 用 .value 包装任意类型,数据已改变不代表 DOM 已同步更新。
原理解释
依赖是怎样建立的?
组件渲染、computed 或 watchEffect 执行时,读取响应式属性会把当前订阅者与属性关联;写入影响该属性的值后,Vue 通知相关订阅者,并通过调度器合并组件更新。可以用“对象 → 属性 → 订阅集合”理解依赖索引,但这不是某一版本完整源码结构的承诺。
ref 与 reactive 怎么选?
ts
import { ref, reactive, toRefs } from 'vue'
const count = ref(0)
count.value++
const state = reactive({ name: 'A', nested: { value: 1 } })
const { name } = toRefs(state)
name.value = 'B' // 更新 state.name
const snapshot = state.name
state.name = 'C'
console.log(snapshot, name.value) // B Cref 能包装原始值与对象,对象默认也会被深层响应式处理;reactive 只接受对象类型,返回代理。普通解构得到的原始值脱离后续属性读取,不能继续跟踪该属性;toRef/toRefs 可保留属性联系。若解构得到的是代理对象,修改其嵌套属性仍可能响应,不能说“一切解构必失效”。
自动解包有什么边界?
模板中顶层 ref 通常无需写 .value,普通 reactive 对象属性中的 ref 也会解包;响应式数组元素和 Map 等集合里的 ref 不按同样规则自动解包。脚本代码直接使用 ref 一般需 .value。
Vue 2 与 Vue 3 有何区别?
Vue 2 主要使用 Object.defineProperty 转换已有属性,并对数组方法做处理,直接新增/删除属性及部分数组下标、长度变更存在检测限制。Vue 3 的 Proxy 能拦截更多对象操作,并支持响应式集合;但原始对象与代理不是同一引用,绕过代理修改原始对象不会因此触发更新。
场景与边界
shallowRef 只跟踪 .value 层替换,适合大型不可变数据或第三方对象;修改内部属性通常不会触发依赖更新。markRaw 可让特定对象跳过代理,但需注意原始对象与代理混用造成身份判断差异。reactive 变量改指向新对象,不会自动重连旧代理上的所有订阅。
项目中的应用
现有三个项目以 React 为主,尚无 Vue 实现证据。可参考 SimulSpeak 字幕状态,练习用 ref 保存字幕列表、按 ID 更新条目;这属于迁移练习,不是已有成果。
常见追问
- 为什么推荐 ref? 易统一处理原始值和整体替换,但 reactive 也适合稳定对象结构;重点是团队约定与边界清楚。
- Proxy 能代理原始值吗? 不能,ref 通过包装提供统一入口。
- Vue 是双向数据流吗? 响应式不等于双向数据流,组件 props 仍是单向下行;v-model 是值与更新事件的组合。