首页
/ Nuxt <NuxtLoadingIndicator> 组件深度指南:页面切换进度条的配置、原理与二次开发

Nuxt <NuxtLoadingIndicator> 组件深度指南:页面切换进度条的配置、原理与二次开发

2026-09-07 18:34:37作者:段琳惟

<NuxtLoadingIndicator> 是 Nuxt 内置的页面级进度指示组件,用于在客户端路由切换期间在页面顶部渲染一条进度条,直观反馈页面加载状态。本文基于本仓库中的官方 API 文档、组件源码、配套 composable 实现与测试用例,系统讲解其标准用法、全部 Props 含义、底层 useLoadingIndicator 工作机制,以及如何基于源码实现完全自定义的加载指示器。

用法:把它放进应用外壳或布局中

在 Nuxt 3.9+(本仓库为 Nuxt 4 版本,特性保持一致)中,该组件是一个内置组件,无需手动导入即可在模板中直接使用。官方文档建议把它放在 app.vue 或 app 目录下的布局文件(layouts)中,使其贯穿所有页面导航过程。

在应用外壳中放置的典型写法如下:

<template>
  <NuxtLoadingIndicator />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

也可以放进某个共享布局内,例如 app/layouts/default.vue,效果等价于在应用外壳中全局挂载。其摆放位置不影响底层逻辑,因为进度条在源码中以 position: fixed 渲染并锚定在视口顶部(见 nuxt-loading-indicator.ts),始终悬浮在页面内容之上。

需要注意的是:该组件是可选组件,即使不放置它,路由切换功能也完全正常,只是缺少了可见的加载反馈。

触发机制:它如何感知页面正在加载

进度条的显示与隐藏并非由组件内部随机触发,而是通过与 Nuxt 运行时 hooks 联动实现的。源码位于 loading-indicator.ts,当运行于客户端时会订阅以下事件:

  • page:loading:start:路由导航开始时触发,调用 start() 让进度条出现;
  • page:loading:end:导航结束(成功或失败)时触发,调用 finish() 让进度条收尾消失;
  • vue:error:应用运行期出现 Vue 错误时,以错误态调用 finish({ error: true }),进度条切换为错误颜色。

page:loading:start / page:loading:end 的发出位置在路由插件中(见 router.ts):router.beforeEach 中调用 page:loading:start第 239-240 行),router.afterEach 中根据导航结果调用 page:loading:end第 168-170 行)。这意味着只要发生了客户端路由跳转,进度条就会自动经历一次完整的"出现 → 前进 → 结束"生命周期,包括嵌套路由、带 query 的导航以及浏览器前进/后退场景。

测试 loading-indicator.test.ts 对该行为做了详细验证:例如在嵌套页面之间导航、携带 query 参数跳转、通过 router.back() 返回等场景下,进度条都会先显示(opacity: 1)再隐藏(opacity: 0),且 page:loading:end 只被触发一次(第 157-173 行)。这些测试也反向印证了上面描述的 hooks 调用链。

Props:完整参数与默认值

官方文档列出了进度条的主要可配置项,结合组件源码(nuxt-loading-indicator.ts 中的 props 定义),所有参数及其默认值如下:

Prop 类型 默认值 说明
color string | boolean 三色渐变(见下) 进度条颜色,可设为 false 关闭显式颜色样式
errorColor string 红系渐变(见下) 当发生错误时进度条切换成的颜色
height number 3 进度条高度(像素)
duration number 2000 预估的加载时长(毫秒),用于计算进度推进速度
throttle number 200 出现/隐藏的节流延时(毫秒),用于避免闪烁
hideDelay number 500 到达 100% 后到开始隐藏的延迟(毫秒),源码补充项
resetDelay number 400 完全隐藏后进度值重置为 0 的延迟(毫秒),源码补充项
estimatedProgress function 内置反切曲线 自定义进度估算函数,见下文

颜色参数的默认值与配置示例

文档说明 color 可被设为 false 以关闭显式颜色,从而让进度条颜色完全由你自己的 CSS 类控制。未配置时的默认样式可以在源码中直接看到:

  • 默认 colorrepeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)
  • 默认 errorColorrepeating-linear-gradient(to right,#f87171 0%,#ef4444 100%)

实际渲染时,进度条根元素上始终带有类名 nuxt-loading-indicator渲染实现见源码 L65-83),因此如果设置 color=false,你可以用一条全局 CSS 规则(如 .nuxt-loading-indicator { background: ... })接管其外观。

常见的基础配置示例:

<template>
  <NuxtLoadingIndicator
    color="repeating-linear-gradient(to right,#42b883 0%,#35495e 100%)"
    error-color="#e74c3c"
    :height="4"
    :duration="3000"
    :throttle="100"
  />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

estimatedProgress:控制进度逼近曲线

文档明确指出,默认情况下 Nuxt 在进度接近 100% 时会"减速退让"(back off),避免长页面加载时进度条过早显示满格。其默认实现可在 composable 源码中看到(defaultEstimatedProgress 函数):

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

这是基于反正切函数 arctan 的平滑曲线:进度前期增长较快,越接近 100% 增长越慢,从而真实反映"长任务未知时长"的特性。若你希望采用线性推进或自定义策略,可通过 estimatedProgress 传入你自己的估算函数,它接收两个参数——加载条的 duration 与已流逝时间 elapsed(单位均为毫秒),并返回 0~100 之间的数值:

<script setup lang="ts">
function myEstimate (duration: number, elapsed: number) {
  return Math.min(100, (elapsed / duration) * 100) // 线性估算
}
</script>

<template>
  <NuxtLoadingIndicator :estimated-progress="myEstimate" />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Slots:默认插槽自定义内部内容

官方文档说明,可以通过默认插槽向进度条传入自定义 HTML 或组件。结合源码(组件渲染逻辑 L83)可见,插槽内容会被渲染在承载渐变背景的容器内部。例如可以在进度条位置放置自己的文本或徽标组件:

<template>
  <NuxtLoadingIndicator>
    <span style="position: fixed; top: 0; right: 8px; font-size: 12px">loading…</span>
  </NuxtLoadingIndicator>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

需要注意的是,容器背景本身是一个基于 backgroundSizescaleX(progress%) 组合实现的进度视觉,插入的内容不会自动跟随进度移动,通常用于在顶部区域叠加品牌元素或文案。

进阶:通过 useLoadingIndicator 手动控制进度

官方文档指出,可以通过 useLoadingIndicator composable 拿到进度条底层实例,从而在任意组件中手动触发 start / finish。这在"需要等待非路由型异步任务"(例如按钮触发的长请求)时非常有用。

返回的状态与方法

该 composable 挂载于全局 Nuxt 实例(nuxtApp._loadingIndicator),并暴露如下只读状态与方法(见 loading-indicator.ts 的类型定义与 useLoadingIndicator 实现 L172-190):

  • isLoadingReadonly<ShallowRef<boolean>>,当前是否处于加载中;
  • errorReadonly<ShallowRef<boolean>>,是否处于错误态;
  • progressReadonly<ShallowRef<number>>,当前进度 0~100;
  • start():置 isLoading 为 true 并开始推进进度,可传 { force: true } 跳过节流立即显示;
  • set(value):把进度设到指定值,同样支持 { force: true }
  • finish():把进度置 100、停止所有定时器并在 500ms 后复位,支持 { force: true }(立即复位)与 { error: true }(以错误态收尾并切换 errorColor);
  • clear():清除 composable 内部使用的定时器与动画帧,供 finish() 内部调用。

它同样会自动监听上面提到的 page:loading:start / page:loading:end hooks 来改变自身状态,因此手动调用与路由导航驱动的进度不会产生逻辑冲突。

手动触发进度的示例

模拟"页面某数据请求较慢"的场景:

<script setup lang="ts">
const { start, finish, error } = useLoadingIndicator()

async function loadHeavyData () {
  start({ force: true })
  try {
    const data = await $fetch('/api/heavy-data')
    // ...处理数据
    finish()
  } catch (e) {
    finish({ error: true })
  }
}
</script>

<template>
  <button @click="loadHeavyData">加载数据</button>
</template>

传入 { force: true }start() 的效果等价于 set(0, { force: true }),即立刻把进度置为 0 并马上显示,不经过默认 200ms 的节流等待。

实现原理:进度推进与显隐的完整生命周期

了解内部实现有助于精确调整参数或编写替代实现。核心逻辑全部集中在 createLoadingIndicator 工厂函数 中,大致分四步:

  1. 启动(set/start):先清掉旧定时器,将 progress 设为起点值,然后等待 throttle(默认 200ms)——若导航在节流窗口内就结束,进度条根本不会出现,从而避免快速跳转时顶部一闪而过。若传 force: true 则节流时间视为 0 立即显示。
  2. 推进(_startProgress):使用 requestAnimationFrame 驱动每帧计算 estimatedProgress(duration, elapsed),并夹在 0~100 之间写入 progress。默认的反正切曲线保证越接近 100% 推进越慢。
  3. 结束(finish):直接把 progress 置 100,取消动画帧与定时器;普通结束走 _hide()——等 hideDelay(500ms)后把 isLoading 置 false,再过 resetDelay(400ms)把进度归零,为下一次导航做准备;错误结束时同时把 error 置 true 以触发 errorColor
  4. 清理:组件卸载或作用域销毁时,订阅的 hooks 会被取消,定时器与动画帧一并清理(依赖计数 _loadingIndicatorDeps 归零后删除全局实例)。

渲染层则利用 CSS transform: scaleX(...) 配合 transform-origin: left 从左侧展开,并用 transition: transform 0.1s, height 0.4s, opacity 0.4s 平滑过渡;不加载时透明度为 0(见组件源码 L65-83)。组件同时通过 expose 暴露了 progressisLoadingerrorstartfinishclear,方便父组件通过模板 ref 直接调用。

自定义:基于源码构建专属加载指示器

由于官方明确将"完全自定义"的途径指向其源码(nuxt-loading-indicator.ts),你可以把 useLoadingIndicator 直接组合进自己的组件——这是一个更推荐的方式,它复用了 hooks 联动与全局单例,无需自己订阅路由事件:

<script setup lang="ts">
// 复用同一全局 loading 实例,自动感知路由导航与 vue:error
const { progress, isLoading, error } = useLoadingIndicator()
</script>

<template>
  <div
    class="my-progress"
    :class="{ visible: isLoading, 'is-error': error }"
    :style="{ width: progress + '%' }"
  />
</template>

<style scoped>
.my-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  background: #42b883;
  opacity: 0;
  transition: width 0.1s ease, opacity 0.4s ease;
}
.my-progress.visible { opacity: 1; }
.my-progress.is-error { background: #e74c3c; }
</style>

然后在 app.vue 中替换官方组件即可获得外观完全由自己掌控、行为与官方一致的加载体验:

<template>
  <MyProgress />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

小结与验证路径

  • 标准接入:将 <NuxtLoadingIndicator /> 放入 app.vue 应用外壳 或 layouts 布局,即可在所有页面导航期间自动展示顶部进度条;
  • 常用调参:通过 color/errorColor/height 控制外观,duration/throttle/estimatedProgress 控制速度与节奏,color=false 时可用 .nuxt-loading-indicator 类完全接管样式;
  • 手动控制:借助 useLoadingIndicatorstart/set/finish 在自定义异步流程中驱动进度并区分错误态;
  • 底层原理:进度条由路由插件在导航前后触发的 page:loading:start/page:loading:end hooks 驱动,内部以 requestAnimationFrame 计算进度,其默认估算曲线为反正切退让曲线,避免长加载过早显示 100%。

如果希望进一步核对实现细节,可依次阅读:组件源码useLoadingIndicator 实现、触发 hooks 的路由插件,以及覆盖嵌套路由/前进后退等真实场景的单元测试

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388