Vue3循环引用组合式函数报错:递归更新超限及返回值带引号
问题描述
在Vue3项目中,我尝试用组合式函数将Proto时间对象转换为格式化的时间字符串,这个函数会随数组元素数量多次调用。但在模板中引用该组合式函数时,出现「Maximum recursive updates exceeded in component」错误,且返回值被引号包裹;而把相同逻辑写成组件方法时,既无错误,返回值也正常。
问题成因
1. 无限递归更新的原因
你的组合式函数里维护了一个共享的adjustedTime ref,每次调用convertTime都会修改这个ref的value值。Vue的模板渲染机制是:当组件状态(比如ref)发生变化时,会触发组件重新渲染;而重新渲染时,模板里的convertTime又会被再次调用,再次修改adjustedTime,触发新一轮渲染——如此循环往复,直到超过Vue的最大更新次数限制,抛出递归更新错误。
而组件方法convertProtoToDate只是单纯返回计算后的字符串,没有修改任何组件状态,所以不会触发额外的渲染,自然不会有递归问题。
2. 返回值带引号的原因
convertTime返回的是adjustedTime这个ref对象,而不是它的value属性。Vue在模板中渲染ref对象时,会自动展开其value,但这里因为你返回的是整个ref(而非直接返回字符串),最终渲染时会把ref的value值以字符串形式输出,表现为带引号的结果;而组件方法直接返回字符串,所以渲染结果正常。
修复方案
组合式函数不需要维护共享的adjustedTime ref,直接返回格式化后的字符串即可。如果需要支持时区的响应式变化,保留timeZone ref即可,每次调用convertTime时用当前的时区值计算:
修改后的组合式函数代码:
import { ref } from "vue"; import moment from "moment"; import "moment-timezone"; export function useAdjustTime() { const timeZone = ref("Europe/Berlin"); function convertTime(t, format = "MMM DD, YYYY hh:mm A z") { const momentTime = moment.utc(t.seconds * 1000 + t.nanos / 1000000); return momentTime.tz(timeZone.value).format(format); } return { timeZone, convertTime }; }
模板中的调用方式保持不变即可:
<template> <div class="mb-5"> <div v-for="(job, i) in jobs" :key="i + 'testjob'"> {{ convertProtoToDate(job.firstVial, "ll") }} {{ convertTime(job.firstVial, "ll") }} <!-- 现在可以正常工作 --> </div> </div> </template>
如果后续需要让所有时间随时区变化自动更新,可以考虑将每个时间的格式化结果转为computed,但当前场景下直接返回字符串的方案最简洁高效。
内容的提问来源于stack exchange,提问作者alex

