先记住这个答案
可写 computed 接收 get 与 set。get 应根据响应式来源纯粹计算结果;给 computed.value 赋值时,会调用 set,由它校验或转换输入并更新真实来源。下一次读取仍由 get 决定,而不是自动保存赋入的原值。两者应共同表达清楚的转换、精度和拒绝规则,使一次写入后的读取符合业务预期。getter 中修改状态或发送请求,会让读取本身产生副作用,破坏缓存和依赖分析。
- computed 的权威数据仍在来源中,setter 不自动保存值
- get 保持纯计算,set 明确输入到源状态的转换
- 允许归一化,但写后读出的结果必须符合可解释规则
用单位换算理解读写映射
假设应用以摄氏温度作为权威来源,却允许用户通过华氏温度编辑。get 负责把摄氏值转换为华氏值,set 则反向转换并更新摄氏来源。computed 不需要再独立保存一份华氏 state,否则就会出现两份数值必须不断同步的问题。
下面示例只演示数值映射,采用有限数检查拒绝无效输入。真实输入框可能产生空字符串和正在编辑的格式,通常应在界面适配层先区分草稿与已确认数字,再交给这个数值接口。单位映射不能自动代替完整的表单编辑协议。
import { computed, ref } from 'vue';
const celsius = ref(0);
const fahrenheit = computed({
get: () => celsius.value * 9 / 5 + 32,
set: (value: number) => {
if (Number.isFinite(value)) celsius.value = (value - 32) * 5 / 9;
}
});
fahrenheit.value = 212;
const afterWrite = { celsius: celsius.value, fahrenheit: fahrenheit.value };
fahrenheit.value = NaN;
const afterInvalid = celsius.value;Vue 3 中写入 212 后,afterWrite 的摄氏值为 100、华氏值为 212;无效数值被拒绝,afterInvalid 仍为 100。示例只进行本地数值操作,没有控制台输出。
归一化和精度需要成为公开约定
有些 setter 会裁剪空格、限制范围或按精度舍入,因此写入某个值后,get 不一定逐字符返回原输入。只要这符合明确业务规则,就不必强求数学上的一一映射。相反,若界面要求保留用户的未完成输入,就应该有独立编辑草稿,而不是让归一化 getter 不断覆盖它。
setter 也不应该写回同一个 computed.value 形成递归。需要更新的是权威 ref、状态字段或明确的父层更新协议。组件 props 为只读时,可写计算常用于把读取映射到 prop、把写入映射到更新事件;它不能直接把 props 的只读约束变成可写权限。
为什么 getter 中的副作用特别难控制
getter 可能因为读取需要而执行,也可能直接复用缓存,执行次数不是用户动作次数。若在其中发送保存请求、修改其它响应式字段或写入当前源状态,普通读取就会启动外部操作,并可能反过来改变自己的依赖,造成循环或难以预测的更新。
异步保存也不宜伪装成一个赋值就已经完成的同步接口。可以让事件或专门动作负责请求、加载、失败与冲突处理,再更新权威状态;computed 负责读取派生展示。setter 中若必须触发命令,也要明确它不提供自动等待、回滚或去重,调用方不能把赋值返回视为远端保存成功。
容易答错的地方
- 给 computed 赋值后,Vue 会自动缓存这个新值
- 赋值调用 setter,后续读取仍通过 getter 和真实来源决定。setter 若没有改变相关来源,展示值可能保持不变,这不一定是响应式失效。
- getter 里顺便修正非法来源最方便
- 读取引发状态修改会让依赖计算与业务校验互相纠缠。应该在输入、更新动作或清楚的校验流程中处理非法状态,让 getter 专注于可重复的纯计算。
面试官还会怎么问?
只读 computed 返回对象后,里面字段也被深冻结吗?
不能把 computed 的只读赋值入口等同于任意返回对象都被深冻结。派生对象仍不应被调用方随意修改,应通过权威来源表达业务变化,具体只读包装按实际 API 判断。
setter 可以拒绝一个值吗?
可以,但要有可见或可检查的反馈契约。静默拒绝在简单示例中能说明边界,实际表单通常还需要错误提示,避免用户不知道为什么输入没有生效。
可写 computed 适合所有双向表单吗?
不适合。转换明确的派生接口很合适;复杂草稿、撤销、异步提交和冲突处理需要独立状态与动作模型,不能全部塞进一个 setter。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。