computed必须基于响应式源数据且为只读ref,setup语法糖中需用computed(() => {})函数形式,取值时JS逻辑写.value、模板中直接用;不可含异步或副作用,赋值会报错。

computed 必须基于响应式源数据(ref 或 reactive),不能直接赋值,也不能含异步逻辑;在 setup 语法糖中必须用 computed(() => {}) 函数调用形式,不是对象写法。
setup 语法糖里怎么写 computed
Vue 3 + uni-app 的 setup 语法糖中,computed 是一个函数,接收一个 getter 函数,返回一个只读的 ref。你得手动加 .value 才能取值(模板里不用)。
- 正确写法:
const fullName = computed(() => firstName.value + ' ' + lastName.value) - 错误写法:
computed: { fullName() { ... } }—— 这是 Options API,setup里不识别 - 模板中直接写
{{ fullName }},不用.value;JS 逻辑里取值必须写fullName.value - 如果依赖的是
reactive对象,记得用可选链或空值检查,比如user?.name?.toLowerCase(),否则user.name.toLowerCase()在user为null时会报Cannot read property 'toLowerCase' of null
computed 里过滤列表常踩的坑
搜索框联动列表过滤是最典型场景,但直接链式调用、类型错位、正则误用会导致运行时崩溃或逻辑失效。
- 输入为空或
null时,this.searchText.trim().toLowerCase()会报错 → 改成this.searchText?.trim()?.toLowerCase() || '' - 字段可能是数字 ID,
item.id.includes(keyword)会失败 → 改成item.id?.toString().includes(keyword) - 别用
new RegExp(keyword, 'i'),用户输[abc]就触发SyntaxError: Invalid regular expression→ 改用字符串includes或预校验后再构造正则 - 不要在
computed里做高亮拼接(如插入<em></em>)→ 高亮是副作用,应交由watch触发更新或模板层用v-html处理
为什么 computed 不能发请求或写 setTimeout
computed 的设计目标是「同步推导只读值」,它的缓存机制只对同步返回值生效。一旦掺入异步或定时器,不仅缓存失效,还会引发竞态、重复执行、[object Promise] 显示等问题。
-
computed(() => await fetch(...))→ 返回 Promise,模板显示[object Promise] -
computed(() => { setTimeout(() => count.value++, 100); return count.value })→ 每次访问都新建定时器,且无法保证执行顺序 - 想根据输入拉远程数据?用
watch(searchText, () => { api.search(...) }, { immediate: true }) - 想防抖?在
watch内部加setTimeout+clearTimeout,别塞进computed
computed 赋值报错 “Cannot assign to read only property” 怎么办
这是最常被忽略的底层约束:computed 返回的是只读 ref,任何尝试赋值(fullName.value = 'xxx')都会在运行时报错。它不是变量,而是派生视图。
- 错误操作:
fullName.value = 'Kobe Bryant'→ 立即抛出TypeError - 正确做法:改源头数据,比如
firstName.value = 'Kobe'或lastName.value = 'Bryant',fullName自动更新 - 需要双向绑定式“可写计算属性”?得显式声明
get和set,但仅限 Options API;setup中需用两个ref+watch模拟,不推荐滥用 - 真正该警惕的是:把
computed当普通变量用,或误以为它支持类似v-model的双向绑定
复杂点在于,很多人把“能自动更新”等同于“能直接修改”,其实 computed 的响应式链条是单向的:只能从源数据流向计算结果,反向不成立。这个边界一旦模糊,后续所有逻辑都会绕弯或出错。


















