You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue中调用Luxon Duration.toFormat触发递归更新超限问题排查

Vue3 + Luxon Duration.toFormat() 触发递归更新警告的原因分析

依赖版本

"dependencies": {
    "luxon": "^3.4.4",
    "vue": "^3.4.21"
}

触发警告的场景

在Vue模板中直接调用Duration属性的toFormat()方法时,页面渲染结果正确,但会抛出递归更新警告:

<template>
    <div>{{entry.duration.toFormat("hh:mm:ss")}}</div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'
import { Duration } from 'luxon'

class Entry {
  public get duration() { return Duration.fromMillis(20000) }
}

const entry = reactive(new Entry())
</script>

警告信息

[Vue warn]: Unhandled error during execution of app errorHandler
Uncaught (in promise) Maximum recursive updates exceeded in component <App>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.

无错误的替代写法

写法1:将toFormat移至getter内部

直接返回格式化后的字符串,无警告:

<template>
    <div>{{entry.duration}}</div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'
import { Duration } from 'luxon'

class Entry {
  public get duration() { return Duration.fromMillis(20000).toFormat("hh:mm:ss") }
}

const entry = reactive(new Entry())
</script>

写法2:将属性改为函数调用

通过函数返回Duration实例再调用toFormat,无警告:

<template>
    <div>{{entry.duration().toFormat("hh:mm:ss")}}</div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'
import { Duration } from 'luxon'

class Entry {
  public duration() { return Duration.fromMillis(20000) }
}

const entry = reactive(new Entry())
</script>

问题原因解析

这不是Luxon的Bug——Luxon的Duration设计确实是不可变的,toFormat()方法不会修改原实例,仅返回新的字符串结果。问题出在Vue响应式系统与Luxon对象的交互逻辑上:

  1. 用reactive包装Entry实例后,Vue会对getter返回的Duration对象做深度响应式代理。
  2. 模板中调用toFormat()时,该方法内部会访问Duration实例的多个内部属性,这些被代理的属性会触发Vue的依赖收集逻辑。
  3. 由于每次访问entry.duration都会返回全新的Duration实例,Vue的响应式系统会误判依赖发生变化,重新触发渲染;而渲染过程中又会再次访问entry.duration并调用toFormat(),形成循环,最终超出递归更新限制。

两种替代写法能解决问题的核心原因:

  • 写法1返回的是字符串原始值,Vue不会对原始值做深度代理,不会触发额外的依赖追踪。
  • 写法2通过函数调用返回Duration实例,Vue不会自动追踪函数调用的依赖,避免了循环触发渲染的问题。

内容的提问来源于stack exchange,提问作者Patrick Hallisey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:42:38