Vue响应式系统
Vue3 如何通过响应式状态驱动视图与副作用更新。
#tech / dev / frame
#resource / vue3
#type / concept
#status / growing
[!info] related notes
Vue响应式系统
Vue 响应式系统的目标是:当状态变化时,让依赖该状态的模板、计算结果和副作用自动更新。
一句话定义
响应式不是“自动刷新页面”这么简单,而是 Vue 把“数据变化”和“依赖更新”建立成了一套可追踪的关系。
和 React 最大的直觉差异
如果你同时学 React 和 Vue,这里最值得抓住的差异是:
- React 更强调“组件重新执行 + Hook 顺序 + 显式依赖”
- Vue 更强调“响应式依赖收集 + 值变化驱动依赖重新计算”
所以 Vue 里很多体验会更像:
- 你声明一个响应式来源
- 依赖它的计算和副作用自动知道自己依赖了什么
而 React 里则更常需要你显式管理:
- state 放哪
- effect 依赖写什么
- 哪些引用需要稳定
Vue3 为什么改用 Proxy
- Vue2 主要依赖
Object.defineProperty - Vue3 改用
Proxy代理整个对象 - 这样可以更自然地监听属性新增、删除、数组变化和更复杂的数据结构
最常用的 5 个响应式 API
ref
适合表示单个响应式值,尤其是基本类型。
const count = ref(0)
count.value++
reactive
适合表示结构化对象状态。
const state = reactive({
count: 0,
user: { name: 'Tom' },
})
computed
用于表达派生状态,而不是重复存储结果。
watch
显式监听某个来源,适合在值变化后执行副作用。
watchEffect
自动收集依赖,适合快速声明一段响应式副作用。
如果要分别看边界和选型,继续读:
最重要的两个边界
派生值优先用 computed
如果一个值可以由其他状态推导出来,就不要再额外存一份。
副作用才用 watch / watchEffect
如果只是为了渲染结果,不要把本该用 computed 表达的逻辑塞进侦听器。
一个更体系化的理解
可以先把 Vue 响应式系统拆成三层:
- 状态来源:
ref/reactive - 派生结果:
computed - 副作用响应:
watch/watchEffect
这和 React 里:
useState- 渲染期派生 /
useMemo useEffect
在问题分层上是相似的,但运行机制不同。
ref 和 reactive 怎么选
- 基本类型优先
ref - 表单对象、页面状态对象常用
reactive - 需要整体替换对象时,
ref({})往往更灵活
常见误区
- 在 JS 中忘记访问
ref.value - 用侦听器保存本来可以直接推导的派生结果
- 在模板里写过重表达式,而不是提取到
computed - 以为响应式等于立即同步更新 DOM,忽略了批量更新与
nextTick
最短记忆方式
ref/reactive:保存状态computed:解释状态watch/watchEffect:响应状态变化后的副作用