VP Loading Indicator 基于 Vue 3,适配 VitePress

发布于 2026年3月4日

VitePress 切换路由时缺少明显的页面反馈,用户会产生“点击了没反应”的错觉。


特性

  • 基于 Vue 3 Composition API
  • 自动监听 VitePress 路由
  • 支持自定义进度计算函数
  • 支持渐变色 / 错误态颜色
  • 内置节流、防闪烁控制
  • 单例模式,多个组件共享实例
  • 使用 requestAnimationFrame 驱动动画

为什么不直接用 NProgress?

确实可以。但我想实现:

  • 更平滑的缓动曲线
  • 更可控的节流与隐藏策略
  • Vue 3 响应式驱动
  • 更优雅的 VitePress 集成方式

于是我自己封装了一个。


核心设计思路

进度计算策略(缓动算法)

默认进度算法:

ts
const defaultEstimatedProgress = (duration: number, elapsed: number): number => {
	const completionPercentage = (elapsed / duration) * 100;
	return (2 / Math.PI) * 100 * Math.atan(completionPercentage / 50);
};

elapsed / duration 得到线性完成度,再通过 atan 映射成「前快后慢」的曲线,避免进度条一开始就冲得很猛、后面却几乎不动,观感更自然。


时序控制(throttle / hideDelay / resetDelay)

  • throttle:点击链接后,先等这么长时间再显示进度条,避免极短请求造成「闪一下就没」。
  • hideDelay:进度到 100% 后,延迟这么久再隐藏条,避免瞬间消失。
  • resetDelay:隐藏之后再过这么久把 progress 置回 0,下次路由切换时从 0 重新开始。

三者配合,既减少闪烁,又让开始和结束都有过渡。


单例与作用域销毁

全局只维护一个 LoadingIndicator 实例,多个地方调用 useLoadingIndicator() 拿到的是同一个实例。用 indicatorUseCount 记录当前有多少组件在用;当这些组件都卸载(onScopeDispose)且计数归零时,才把单例置空,避免在 SPA 里重复注册路由钩子、产生多个进度条逻辑。


组件 API(Props)

属性类型默认值说明
throttlenumber200显示进度条前等待时间(ms)
durationnumber2000进度条动画总时长(ms)
hideDelaynumber500到 100% 后延迟隐藏(ms)
resetDelaynumber400隐藏后延迟重置进度(ms)
heightnumber3进度条高度(px)
colorstring | boolean见下进度条颜色/渐变,false 用默认渐变
errorColorstring红色渐变错误态时的颜色/渐变
estimatedProgress(duration, elapsed) => number内置 atan 曲线自定义进度计算函数

默认 color 为 Nuxt 风格渐变:repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)


useLoadingIndicator 返回值

在非 SSR 环境下,createLoadingIndicator 内部会挂到 VitePress 的 router.onBeforeRouteChange / router.onAfterRouteChange,所以只挂一个进度条组件就会在路由切换时自动跑。

若需要手动控制(例如在非 VitePress 的 Vue 应用里),可以使用返回的方法:

属性/方法类型说明
progressShallowRef<number>当前进度 0–100
isLoadingShallowRef<boolean>是否显示进度条
errorShallowRef<boolean>是否错误态(可配合 errorColor
start()() => void从 0 开始进度条
end()() => void直接到 100% 并进入隐藏流程
setIndicator(at)(at: number) => void设置初始进度再启动
clearStart()() => void取消尚未开始的 throttle/RAF

在 VitePress 中集成

在主题的根布局里挂一个组件即可,建议包在 ClientOnly 里,避免 SSR 报错:

vue
<template>
	<ClientOnly>
		<VPLoadingIndicator color="var(--color-blog-accent)" />
	</ClientOnly>
	<!-- 其余 layout -->
</template>

<script setup>
import VPLoadingIndicator from 'vp-loading-indicator';
</script>

按需传入 heightcolorerrorColor 等即可与站点风格统一。


组件与工具代码结构

进度条组件:根据 useLoadingIndicatorprogressisLoadingerror 渲染一根固定在顶部的条,用 transform: scaleX(progress%) 做宽度动画,用 backgroundSize 配合渐变实现「条纹向右推进」的效果。

工具层createLoadingIndicator(opts) 里用 requestAnimationFrame 循环调用 getProgress(duration, elapsed) 更新 progressstart 时先 throttle 再开 RAF,end 时设 100%、清定时器、走 hide → reset 的延迟链。非 SSR 时把 start / end 挂到 router.onBeforeRouteChange / router.onAfterRouteChange,即可与 VitePress 路由联动。


小结

VP Loading Indicator 用 Vue 3 响应式 + 单例 composable + VitePress 路由钩子,实现了一个可配置、可扩展的顶部加载条:默认 atan 缓动、可自定义进度函数,throttle/hideDelay/resetDelay 控制时序,单例在组件全部卸载后自动清理。如果你也在用 VitePress 或 Vue 3 文档站,不妨按需接入或在此基础上改一版自己的进度条。

项目地址:VPLoadingIndicator

cd . .
Copyright © 2025-present Joseph Joestar 蜀ICP备2025171383号