- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
useNow是 VueUse 提供的一个轻量级组合式函数,用于在 Vue 3 应用中获取响应式的当前 Date 实例。它默认通过requestAnimationFrame驱动时间刷新,并提供pause/resume/isActive等控制能力,非常适合时钟、计时器、倒计时、时间轴等实时渲染场景。读完本文,你将掌握useNow的基础用法、controls与自定义scheduler配置、组件式调用方式,以及其底层基于shallowRef与调度器的实现原理。
快速上手:基础用法
useNow的定位非常纯粹——"Reactive current Date instance"(响应式的当前日期实例)。最简单的调用方式如下:
import { useNow } from '@vueuse/core' const now = useNow()调用后,now是一个ShallowRef<Date>,其值会随着当前时间的推移而不断更新。在模板中直接使用即可:
<script setup lang="ts"> import { useNow } from '@vueuse/core' const now = useNow() </script> <template> <div>Now: {{ now }}</div> </template>对应的官方演示见 demo.vue,这正是该组合式函数最典型的应用场景。由于useNow依赖浏览器端的requestAnimationFrame,它主要面向客户端渲染环境(对应测试文件也命名为index.browser.test.ts)。
选项与返回类型
useNow的完整签名如下(见 index.ts):
export function useNow(options?: UseNowOptions<false>): ShallowRef<Date> export function useNow(options: UseNowOptions<true>): { now: ShallowRef<Date> } & Pausable它接受一个可选的UseNowOptions,目前包含两个配置项:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
controls | boolean | false | 是否暴露pause、resume、isActive等控制能力 |
scheduler | (cb: Fn) => Pausable | useRafFn | 自定义调度器,决定时间值如何被驱动刷新 |
其中controls会通过**条件类型(conditional type)**影响返回值结构。从源码UseNowReturn的定义(index.ts)可以看出:
controls: false(默认)时返回ShallowRef<Date>,即直接返回时间 ref;controls: true时返回{ now: ShallowRef<Date> } & Pausable,即解构出now以及pause、resume、isActive。
启用 controls
import { useNow } from '@vueuse/core' const { now, pause, resume } = useNow({ controls: true })这是官方文档给出的第二种用法。拿到控制能力后,你可以随时暂停时间刷新:
// 暂停更新时间 pause() // 恢复更新时间 resume() // 读取当前是否处于刷新状态 // isActive: Readonly<ShallowRef<boolean>>Pausable接口定义在 packages/shared/utils/types.ts,包含三个成员:
isActive:只读的ShallowRef<boolean>,标识调度器当前是否在运行;pause:临时暂停效果执行;resume:恢复效果执行。
源码原理:shallowRef + 调度器
useNow的实现非常精简(index.ts),核心逻辑只有三步:
export function useNow(options: UseNowOptions<boolean> = {}): UseNowReturn<boolean> { const { controls: exposeControls = false, scheduler = useRafFn, } = options const now = shallowRef(new Date()) const update = () => now.value = new Date() const controls = scheduler(update) if (exposeControls) { return { now, ...controls, } } else { return now } }三个关键设计点值得注意:
1. 使用shallowRef而非ref。now存储的是一个Date对象,但每次刷新时都是整体替换为新的new Date()实例,并不涉及对 Date 内部属性的深层追踪,因此用shallowRef即可获得最优性能,避免不必要的深层响应式开销。源码中useNow被标记了@__NO_SIDE_EFFECTS__,意味着在构建时会被识别为无副作用函数,便于摇树优化。
2. 刷新逻辑与调度器解耦。update = () => now.value = new Date()只是单纯地把当前时间写入 ref,至于"多久更新一次"完全由scheduler决定。默认调度器是useRafFn,即每一帧(约 60fps)刷新一次。
3.controls展开传递。当exposeControls为true时,直接把调度器返回的Pausable控制项展开到返回值中,实现{ now, pause, resume, isActive }的结构。
默认调度器 useRafFn 的底层行为
默认调度器useRafFn实现在 useRafFn/index.ts,它基于浏览器的requestAnimationFrameAPI 工作,并通过isActive、previousFrameTimestamp和rafId维护循环状态。其关键行为包括:
- 创建时默认立即启动(
immediate = true),并调用tryOnScopeDispose(pause),因此在组件卸载或作用域销毁时会自动停止刷新,无需手动清理; - 支持
fpsLimit限制每秒执行帧数(默认null不限制),内部通过比较delta与1000 / limit决定是否跳过当前帧; pause()会调用window.cancelAnimationFrame(rafId)取消下一帧注册,resume()则重置时间戳后重新注册。
由于这些行为被封装成Pausable,useNow得以完全复用而无需重复实现定时逻辑。
自定义调度器:控制刷新频率
scheduler选项的存在让useNow的刷新策略变得完全可定制。任何符合(cb: Fn) => Pausable签名的函数都可以作为调度器,例如改用useIntervalFn按固定时间间隔刷新(这在useNow的官方测试 index.browser.test.ts 中有直接验证):
import { useNow } from '@vueuse/core' import { useIntervalFn } from '@vueuse/shared' // 每 1000ms 刷新一次时间 const { now } = useNow({ controls: true, scheduler: cb => useIntervalFn(cb, 1000), })甚至可以实现"惰性启动"——默认调度器useRafFn的immediate为true,创建即开始刷新;如果你希望一开始不刷新、等到某个时机再resume(),可以这样写(同样有测试用例覆盖,见 index.browser.test.ts):
import { useNow } from '@vueuse/core' import { useRafFn } from '@vueuse/core' const { now, resume } = useNow({ controls: true, scheduler: cb => useRafFn(cb, { immediate: false }), })测试验证了该场景下:创建后即使推进 50ms,now值也保持不变;调用resume()后时间才开始前进。
注意:
scheduler的类型ConfigurableScheduler定义于 packages/core/_configurable.ts,即(cb: Fn) => Pausable,要求返回的调度器必须同时具备暂停与恢复能力,这保证了controls模式下pause/resume始终可用。
组件式用法:<UseNow>
除了组合式函数,VueUse 还为useNow提供了对应的渲染函数组件UseNow(见 component.ts)。该组件将useNow({ controls: true })的返回值用reactive()包装后,通过作用域插槽暴露给模板:
<template> <UseNow v-slot="{ now, pause, resume }"> Now: {{ now }} <button @click="pause()"> Pause </button> <button @click="resume()"> Resume </button> </UseNow> </template>组件内部逻辑(component.ts)非常直白:接收scheduler属性透传给useNow,并强制controls: true以获得完整控制项;然后通过slots.default(data)将响应式数据注入插槽。由于组件始终开启 controls,插槽里总是能拿到now、pause、resume、isActive。
行为验证与测试保障
useNow的行为在 index.browser.test.ts 中得到了系统验证,测试使用vi.useFakeTimers()模拟时间推进,覆盖了四条核心路径:
- 默认行为:
useNow()返回的时间戳不大于当前Date.now(); - 惰性调度器:
immediate: false时时间不前进,resume()后才前进; - 组件渲染:
<UseNow>通过作用域插槽正确输出时间; - 控制能力:对
useRafFn与useIntervalFn(cb, 50)两种调度器分别验证pause()后时间冻结、resume()后继续前进。
这些测试从侧面印证了useNow的接口契约:默认即用、可暂停可恢复、调度器可替换。
相关资源索引
- 文档:packages/core/useNow/index.md
- 实现源码:packages/core/useNow/index.ts
- 组件封装:packages/core/useNow/component.ts
- 演示示例:packages/core/useNow/demo.vue
- 浏览器测试:packages/core/useNow/index.browser.test.ts
- 默认调度器:packages/core/useRafFn/index.ts
- 可配置类型与默认窗口:packages/core/_configurable.ts
Pausable类型定义:packages/shared/utils/types.ts- 导出入口:packages/core/index.ts(
export * from './useNow')
useNow通过"响应式 ref + 可替换调度器"的简洁抽象,把"当前时间"这个最常见的实时数据源变成了一个可组合、可控制、可测试的 Vue 响应式原语。无论是做简单时钟,还是构建需要精确控制刷新节奏的时间组件,它都是一个值得优先选择的基础工具。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
wagmi Vue useChainId 组合式函数:响应式获取当前链 ID 的完整指南
wagmi Vue useChainId 组合式函数:响应式获取当前链 ID 的完整指南 在 @wagmi/vue 中, useChainId 是获取并实时监听
区块链Web3前端如何为Mi-Create开发插件:PluginAPI接口与.plg打包完全指南
如何为Mi Create开发插件:PluginAPI接口与.plg打包完全指南 Mi Create 是一款面向小米可穿戴设备(2021年及以后机型)的开源表盘创
前端PyPTO Tensor 操作全指南:数学运算与逻辑结构变换实战
PyPTO Tensor 操作全指南:数学运算与逻辑结构变换实战 PyPTO 作为 Parallel Tensor/Tile Operation 编程范式,为昇
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考