computed 依赖必须是响应式数据才能触发更新。它通过依赖收集自动追踪 ref、reactive、torefs 处理的 props 等响应式来源,普通变量、字面量、未包裹对象等无法被监听,修改后 computed 不会重新计算。

因为 computed 的缓存和自动更新机制完全依赖 Vue 的响应式系统。它不是靠“定时检查”或“手动通知”,而是靠在计算过程中**自动追踪你读取了哪些响应式数据**——只有被 track 到的响应式依赖变化时,computed 才会标记为“脏”,下次访问才重新执行。
响应式依赖是 computed 的“触发开关”
Vue 在 computed 函数首次执行时,会开启依赖收集(track)。此时如果你读取的是 ref.value、reactive 对象的属性、或者 Pinia store 中的状态,这些访问会被记录下来。之后只要其中任何一个发生变化,trigger 就会通知这个 computed 该更新了。
但如果你读取的是普通 JS 变量(比如 const name = 'Alice')、函数参数、或未通过 ref/reactive 包裹的字面量对象,它们不在响应式系统里,Vue 根本无法监听,也就不会触发重新计算——哪怕你改了值,computed 还是返回旧结果。
常见非响应式依赖陷阱
-
直接用普通变量或字面量:
const filter = 'active'; const filteredList = computed(() => list.filter(item => item.status === filter))—— filter 不是响应式,改 filter 值毫无作用 -
从 props 解构出的普通值:
const { id } = toRefs(props); const userInfo = computed(() => apiMap[id.value])—— 如果没用 toRefs 或 toRef 包裹,id 就只是个普通字符串,不响应 -
外部模块导入的常量或工具函数返回值:
import { DEFAULT_PAGE } from '@/config'; const page = computed(() => DEFAULT_PAGE + 1)—— DEFAULT_PAGE 是普通导出,不会响应式更新 -
数组/对象未用 reactive/ref 包裹就直接赋值:
let data = { count: 0 }; const doubled = computed(() => data.count * 2)—— data 是普通对象,data.count 改变不会触发 computed 更新
怎么快速判断一个值是不是响应式?
看它是否满足以下任一条件:
- 由
ref()创建,且你访问的是.value(如count.value) - 由
reactive()创建,且你访问的是其内部属性(如user.name) - 来自
props且已用toRefs()或toRef()处理过 - 来自
useStore()(Pinia)或useRouter()等组合式 API 返回的响应式对象
不确定时,可以在控制台打印它的 constructor:响应式对象通常是 RefImpl 或 ReactiveEffect 实例;普通变量就是 String、Number、Object 等原生类型。
修复非响应式依赖的常用方法
- 把普通变量转成 ref:
const filter = ref('active'),然后在 computed 中用filter.value - 对 props 解构使用
toRefs:const { id, type } = toRefs(props),再在 computed 中读id.value - 需要响应式地使用配置项,不要直接 import 常量,改用
provide/inject或封装成可响应的 composable - 普通对象想响应化?用
reactive({ ...obj });基础类型用ref(val)










