为何此CodeSandbox示例中仅number变量响应式失效?
你碰到的这个响应式差异问题,核心在于两个composable里ref的更新逻辑和Vue响应式追踪的交互方式不一样:
为什么randomNumber能保持响应式?
在useRandomNumber里,setInterval是调用这个composable时(也就是setup执行阶段)就启动的。此时randomNumber这个ref已经被返回给setup,模板也已开始渲染,Vue的响应式系统已经追踪到randomNumber和模板的绑定关系。所以每次setInterval修改randomNumber.value时,Vue能立刻检测到变化,触发模板更新。
为什么number的响应式会失效?
在useNumber里,addNumber函数修改的是composable内部ref的value,但你通过spread展开返回useNumber()的结果时,模板里绑定的number是Vue自动解包后的普通数值,而非响应式访问器。
简单说:addNumber改的是原始ref的value,但模板显示的是初始解包后的普通值,这个普通值不会自动跟着ref的变化更新——Vue的自动解包只在初始渲染时建立一次关联,后续ref的value变化没法同步到这个普通值上。
修复方法
有两种简单的修复方式:
不使用spread展开,直接返回composable的对象:
setup() { const numberStore = useNumber() const randomStore = useRandomNumber() return { numberStore, randomStore } }模板里对应改成
{{ numberStore.number }}和@click="numberStore.addNumber"。直接返回ref和函数,不用spread包裹:
setup() { const { number, addNumber } = useNumber() const { randomNumber } = useRandomNumber() return { number, addNumber, randomNumber } }这种写法和spread看似类似,但Vue对直接返回的ref处理更可靠,能正确维持解包后的响应式关联。
内容的提问来源于stack exchange,提问作者Tom De Smet

