深浅主题
Vue 响应式失效
问题现象
接口返回了新值,但页面仍显示旧值;控制台变量变化与 DOM 更新不一致。先确认组件渲染读取的是否就是被修改的状态。
适用环境
Vue 3,Composition API;示例采用普通 reactive 对象,不涉及 Pinia store 解构。
本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。
复现或诊断步骤
- 用 Vue Devtools 查看组件状态,在点击前后记录同一字段。
- 搜索 reactive 对象的解构、重新赋值以及对原始对象的写入。
- 在最小组件中保留一个数字字段和按钮;若最小示例正常,逐步恢复业务转换函数。
原因分析
从 reactive 对象解构基本类型会得到当时的值,后续赋值不再经过代理。把局部变量改为另一个 reactive 对象,也不会让原有消费者自动订阅新对象。DOM 异步刷新则属于另一类情况,可在 nextTick 后读取。
修复示例
ts
import { reactive, toRefs, nextTick } from 'vue'
const state = reactive({ count: 0, name: '' })
const { count } = toRefs(state)
async function update() {
count.value++
await nextTick()
// 此处再测量依赖 count 的 DOM
}
function apply(data: { count: number; name: string }) {
Object.assign(state, data)
}需要整体替换的数据可使用 ref,并给 .value 赋新对象。需要保留对象身份时更新其属性;Object.assign 不会自动删除旧字段,重置表单时应明确列出字段或使用默认值合并。
验证方法
连续修改字段、重置和异步回填,确认 Devtools 与页面一致。增加 nextTick 只用于判断刷新时机;去掉它后常规文本渲染也应正常。
常见误区
不要用强制刷新页面掩盖引用断开;不要把 Vue 2 的数组索引限制直接套到 Vue 3。对原始对象的写入与对代理的写入也不能混为一谈。