ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

airi 实战:VueUse `useTimeAgo` 响应式相对时间格式化完全指南

airi 实战:VueUse `useTimeAgo` 响应式相对时间格式化完全指南 airi 实战VueUseuseTimeAgo响应式相对时间格式化完全指南【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airiuseTimeAgo是 VueUse 在 Time 分类下提供的一个响应式组合式函数用于把某个时间点实时渲染为xx 秒前 / xx 分钟前 / xx 小时前这类人类可读的相对时间字符串并且在时间流逝时自动更新。在 airi 这类基于 Vue 3 构建、横跨 Web / Electron / Capacitor 多端场景的 monorepo 中无论是会话列表的最后活跃时间、消息时间戳还是直播状态标签都可以用它替代手写的setInterval轮询逻辑。读完本文你将掌握useTimeAgo的组合式用法、UseTimeAgo组件用法、非响应式的formatTimeAgo一次性格式化以及max、rounding、messages、units、controls等完整选项体系并了解 airi 仓库中可与其对照的手写相对时间实现。一、为什么需要响应式相对时间常规的时间前字符串很容易写但难点在于实时性页面停留在会话列表里时间戳却在不断翻新必须每隔一段时间重新计算一次。手写方案通常要自己维护定时器、处理组件卸载清理、还要考虑精确到秒还是分钟。airi 项目对 VueUse 有完整的依赖基础vueuse/core通过 pnpm catalog 统一管理见 pnpm-workspace.yaml版本为^14.4.0并被apps/stage-web、apps/stage-pocket、apps/stage-tamagotchi、packages/stage-ui、packages/stage-pages、packages/electron-vueuse等 20 余个包引用例如 packages/stage-ui/package.json。也就是说airi 的前端代码在任意应用、任意包中都可以直接引入useTimeAgo无需新增依赖。useTimeAgo的核心价值有两点响应式输入传入MaybeRefOrGetterDate | number | string时间源本身可以是 ref发生变化时输出同步更新自动刷新输出即使时间源不变它也会按调度周期自动重算输出让x 秒前持续翻新。二、组合式函数基础用法先看文档给出的最小示例对应关联文档 useTimeAgo.md 的 Usage 部分import { useTimeAgo } from vueuse/core const timeAgo useTimeAgo(new Date(2021, 0, 1))useTimeAgo接受Date、时间戳数值或可被解析的字符串返回值默认是一个ComputedRefstringconst timeAgo useTimeAgo(new Date(2021, 0, 1)) // timeAgo.value 会随着当前时间推移自动变化例如 5 years ago在 Vue 模板中直接使用script setup langts import { useTimeAgo } from vueuse/core const lastSeen useTimeAgo(chatSession.updatedAt) /script template spanLast active {{ lastSeen }}/span /template也可以传入一个 ref 作为时间源时间源变化时输出会随之更新import { ref, watch } from vue import { useTimeAgo } from vueuse/core const updatedAt refnumber(Date.now()) const timeAgo useTimeAgo(updatedAt) updatedAt.value Date.now() 3_600_000 // 改为 1 小时后timeAgo 自动变为未来时间描述从类型声明可以确认详见下文类型系统一节time参数的类型是MaybeRefOrGetterDate | number | string因此直接传ref或getter都是合法的。三、UseTimeAgo组件用法VueUse 为几乎所有核心函数都提供了等价的渲染函数组件形式useTimeAgo也不例外。在模板中通过v-slot拿到格式化结果template UseTimeAgo v-slot{ timeAgo } :timenew Date(2021, 0, 1) Time Ago: {{ timeAgo }} /UseTimeAgo /templateUseTimeAgo组件接收与useTimeAgo相同的选项作为 props如:time、:max、:show-second、:rounding等通过作用域插槽把timeAgo暴露给模板。这种写法适合在模板中局部使用、且不想在script setup里额外声明变量的场景。VueUse 组件默认是 tree-shakable 的按需引入不会增大打包体积。四、非响应式用法formatTimeAgo如果只是在某个一次性渲染的场合例如v-for中渲染静态历史记录、或 SSR 首屏输出获取一个相对时间字符串而不需要它随时间自动刷新可以使用formatTimeAgo它直接返回普通string而不是Refimport { formatTimeAgo } from vueuse/core const timeAgo formatTimeAgo(new Date(2021, 0, 1)) // string例如 5 years agoformatTimeAgo是纯函数签名如下export declare function formatTimeAgo UnitNames extends string UseTimeAgoUnitNamesDefault, ( from: Date, options?: FormatTimeAgoOptionsUnitNames, now?: Date | number, ): string第三个参数now允许你传入自定义的当前时间基准点这在测试、快照、以及需要固定时间基准的离线场景例如 airi 的本地运行日志回放中非常实用——不传时默认取Date.now()。五、选项详解从默认值到完整行为useTimeAgo与formatTimeAgo共享FormatTimeAgoOptions组合式版本在此基础上叠加了调度与控制相关选项UseTimeAgoOptions。以下参数均以关联文档的类型声明注释为基准5.1max超过阈值显示完整日期/** * Maximum unit (of diff in milliseconds) to display the full date instead of relative * default undefined */ max?: UnitNames | number当时间差超过max指定的单位或毫秒数时不再输出x 年前而是调用fullDateFormatter输出完整日期。默认undefined表示始终使用相对格式。5.2fullDateFormatter完整日期的自定义格式化fullDateFormatter?: (date: Date) string用于自定义超过max阈值后完整日期的输出格式默认使用内置的日期格式化逻辑。airi 中已有类似的Intl.DateTimeFormat用法可以参考例如 apps/ui-server-auth/src/pages/profile.vue 用dateStyle: long格式化用户资料日期packages/stage-pages/src/pages/settings/account/account-settings-page.vue 用dateStyle: medium处理账号相关日期。如果希望超过 7 天就显示完整日期可以组合使用const timeAgo useTimeAgo(timestamp, { max: week, fullDateFormatter: date new Intl.DateTimeFormat(undefined, { dateStyle: long }).format(date), })5.3messages自定义文案messages?: UseTimeAgoMessagesUnitNamesUseTimeAgoMessages在类型上由两部分组成export interface UseTimeAgoMessagesBuiltIn { justNow: string past: string | UseTimeAgoFormatterstring future: string | UseTimeAgoFormatterstring invalid: string } export type UseTimeAgoMessages UnitNames extends string UseTimeAgoUnitNamesDefault, UseTimeAgoMessagesBuiltIn RecordUnitNames, string | UseTimeAgoFormatternumber即除了justNow刚刚、past过去、future未来、invalid非法时间四个内置键之外还需要为second/minute/hour/day/week/month/year每个单位提供格式化模板或格式化函数。其中export type UseTimeAgoFormatterT number ( value: T, isPast: boolean, ) stringisPast表示该时间点是否早于当前时间可用于区分x 天后与x 天前。一个中文自定义文案示例import { useTimeAgo } from vueuse/core const timeAgo useTimeAgo(new Date(Date.now() - 10 * 60 * 1000), { messages: { justNow: 刚刚, past: (value) ${value}前, future: (value) ${value}后, invalid: 时间无效, second: (value, isPast) ${value} 秒${isPast ? 前 : 后}, minute: (value, isPast) ${value} 分钟${isPast ? 前 : 后}, hour: (value, isPast) ${value} 小时${isPast ? 前 : 后}, day: (value, isPast) ${value} 天${isPast ? 前 : 后}, week: (value, isPast) ${value} 周${isPast ? 前 : 后}, month: (value, isPast) ${value} 个月${isPast ? 前 : 后}, year: (value, isPast) ${value} 年${isPast ? 前 : 后}, }, })注意past/future的 formatter 类型是UseTimeAgoFormatterstring即输入为字符串形式的值可用于定义整体前后缀规则。5.4showSecond是否精确到秒/** * Minimum display time unit (default is minute) * default false */ showSecond?: boolean默认false时最小显示单位是分钟——刚刚附近的时间会显示为justNow设为true后可以显示x 秒前适合需要秒级感知的场景如实时直播、语音通话状态。5.5rounding取整方式/** * Rounding method to apply. * default round */ rounding?: round | ceil | floor | number控制差值换算成单位数量时的取整策略默认round四舍五入。可选项round四舍五入例如 89 秒显示为 1 minute agoceil向上取整例如 61 秒显示为 2 minutes agofloor向下取整例如 89 秒显示为 1 minute ago与 round 在边界上有差异number直接传入数字时按该数值对结果做精度控制配合自定义需求使用。5.6units自定义时间单位表export interface UseTimeAgoUnitUnit extends string UseTimeAgoUnitNamesDefault { max: number value: number name: Unit } units?: UseTimeAgoUnitUnitNames[]units是数组每个元素包含字段含义max该单位覆盖的最大毫秒差value该单位对应的毫秒换算值name单位名称参与 messages 的键匹配airi 仓库的 packages/stage-ui/src/components/scenarios/chat/components/sessions-drawer.vue 中有一段手写的RELATIVE_UNITS常量结构与UseTimeAgoUnit高度一致可以作为自定义units的换算参照const RELATIVE_UNITS: Array[Intl.RelativeTimeFormatUnit, number] [ [year, 31_536_000_000], [month, 2_592_000_000], [week, 604_800_000], [day, 86_400_000], [hour, 3_600_000], [minute, 60_000], ]对应地useTimeAgo的默认单位表可以理解为由second1_000ms到year约 31_536_000_000ms组成的递减层级每个单位的max通常是下一更大单位的值。5.7controls与updateInterval/scheduler组合式版本独有export interface UseTimeAgoOptions Controls extends boolean, UnitNames extends string UseTimeAgoUnitNamesDefault, extends FormatTimeAgoOptionsUnitNames, ConfigurableScheduler { /** * Expose more controls * default false */ controls?: Controls /** * Intervals to update, set 0 to disable auto update * deprecated Please use scheduler option instead * default 30_000 */ updateInterval?: number }controls设为true时返回值变为对象形态同时获得Pausable能力可暂停/恢复自动刷新type UseTimeAgoReturnControls extends boolean false Controls extends true ? { timeAgo: ComputedRefstring } Pausable : ComputedRefstringconst { timeAgo, pause, resume, isActive } useTimeAgo(timestamp, { controls: true }) // 需要冻结时间显示时调用 pause()恢复自动刷新时调用 resume()updateInterval刷新间隔毫秒数默认30_00030 秒设为0可关闭自动更新注意该选项在类型声明中标记为deprecated新代码应改用schedulerConfigurableScheduler来精细控制刷新调度。从实现可以推断useTimeAgo内部通过类似useIntervalFn/useRafFn的调度机制驱动输出重算因此即使时间源从未变化x 秒前的文案也会按周期翻新。六、类型系统速查关联文档完整给出了公开类型声明整理如下便于在自定义封装时正确引用类型说明UseTimeAgoFormatterT(value: T, isPast: boolean) string消息格式化函数UseTimeAgoUnitNamesDefault默认单位名second \| minute \| hour \| day \| week \| month \| yearUseTimeAgoMessagesBuiltIn内置四键justNow/past/future/invalidUseTimeAgoMessagesUnitNames内置键 每个单位名到消息模板/函数的映射FormatTimeAgoOptionsUnitNamesmax/fullDateFormatter/messages/showSecond/rounding/unitsUseTimeAgoOptionsControls, UnitNames在FormatTimeAgoOptions基础上增加controls、updateIntervaldeprecated并继承ConfigurableSchedulerUseTimeAgoUnitUnit{ max: number; value: number; name: Unit }UseTimeAgoReturnControlscontrols: true时为{ timeAgo } Pausable否则为ComputedRefstring两个函数的重载签名useTimeAgoUnitNames extends string UseTimeAgoUnitNamesDefault( time: MaybeRefOrGetterDate | number | string, options?: UseTimeAgoOptionsfalse, UnitNames, ): UseTimeAgoReturnfalse useTimeAgoUnitNames extends string UseTimeAgoUnitNamesDefault( time: MaybeRefOrGetterDate | number | string, options: UseTimeAgoOptionstrue, UnitNames, ): UseTimeAgoReturntrue formatTimeAgoUnitNames extends string UseTimeAgoUnitNamesDefault( from: Date, options?: FormatTimeAgoOptionsUnitNames, now?: Date | number, ): stringUnitNames泛型允许你扩展自定义单位名配合自定义units与messages从而获得完整的类型推导。七、i18n 场景的姊妹方案useTimeAgoIntlairi 是典型的多语言项目docs/content下同时维护 en / ja / ko / zh-Hans 等多语言文档如果你需要跟随用户 locale 输出本地化相对时间VueUse 还提供了基于Intl.RelativeTimeFormat的姊妹函数useTimeAgoIntl参见同目录参考文档 useTimeAgoIntl.mdimport { useTimeAgoIntl } from vueuse/core const timeAgoIntl useTimeAgoIntl(new Date(2021, 0, 1))它支持locale如zh-CN、ja-JP、relativeTimeFormatOptions、insertSpace、joinParts等选项由浏览器原生Intl负责文案与语法。非响应式版本为formatTimeAgoIntl/formatTimeAgoIntlParts。选择建议需要完全自定义文案品牌化、术语统一→useTimeAgomessages需要跟随系统/用户 locale 自动本地化 →useTimeAgoIntl两者都支持controls与调度选项都可替代项目中的手写相对时间逻辑。八、airi 中的实践对照手写实现 vsuseTimeAgo在 packages/stage-ui/src/components/scenarios/chat/components/sessions-drawer.vue 中会话列表的最后更新时间标签目前是手写实现的function formatUpdatedAt(ts: number): string { const formatter new Intl.RelativeTimeFormat(undefined, { numeric: auto }) const delta ts - Date.now() const abs Math.abs(delta) for (const [unit, ms] of RELATIVE_UNITS) { if (abs ms) { const value Math.round(delta / ms) return formatter.format(value, unit) } } return formatter.format(0, second) }这段代码每次rows重新计算时才执行一次本身并不随时间自动翻新——要获得与useTimeAgo相同的自动刷新体验还需额外配合定时器触发重算。这正是useTimeAgo能直接改进的点const updatedAtLabel computed(() useTimeAgo(meta.updatedAt, { showSecond: false }).value, )借助useTimeAgo输出会按默认 30 秒间隔自动刷新且无需手动管理定时器与组件卸载清理。此外 packages/stage-ui/src/components/modules/GamingMinecraft.vue 使用Intl.DateTimeFormat格式化 Minecraft 流量时间戳、apps/ui-server-auth/src/pages/profile.vue 使用dateStyle: medium格式化资料时间这些绝对时间场景则仍适合保留Intl.DateTimeFormat与useTimeAgo的相对时间场景互补。九、最佳实践小结默认每 30 秒刷新对于分钟级展示足够需要秒级精度时开启showSecond: true并考虑缩短updateInterval或改用scheduler。批量历史记录用formatTimeAgov-for渲染固定列表时使用非响应式版本避免大量实例同时跑定时器。需要暂停刷新时用controls: true例如会话详情页切到后台可以pause()冻结显示、resume()恢复。多语言优先useTimeAgoIntlairi 这类多语言应用让Intl.RelativeTimeFormat承担本地化职责最省心需要统一品牌文案时再自定义messages。扩展单位时保持类型一致自定义units后用UnitNames泛型约束messages让编译期帮你校验单位名是否齐全。至此从最小示例、组件用法、非响应式函数到max/rounding/units/messages/controls等完整选项以及 i18n 姊妹方案与 airi 仓库内的实践对照useTimeAgo的全貌已经清晰它把相对时间 自动刷新这件事封装成了开箱即用的响应式能力是 Vue 应用中处理时间展示最省心的选择。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表