脚本专家 发表于 2026-6-12 17:00:02

Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫

在Vue3项目中,一个运行中的进度条能显著提升用户体验,尤其适合长耗时请求或页面切换场景。NProgress作为轻量级进度条库,搭配TypeScript和Vite环境,可优雅地融入现有项目。下面分享一套经过验证的集成方案,涵盖基础安装、可配置Hook封装、Axios拦截器和Vue Router守卫集成,以及全局样式自定义。

一、环境准备
首先安装核心依赖和类型定义:

pnpm i nprogress lodash-es
pnpm i @types/nprogress @types/lodash-es -D

nprogress提供进度控制API,lodash-es用于对象深度合并。类型声明仅在开发时使用。

通过环境变量控制进度条开关,在.env文件中加入:

VITE_ROUTER_NPROGRESS = true
VITE_REQUEST_NPROGRESS = true

设置为'false'即可关闭对应场景的进度条,无需修改代码。

二、核心封装:useProgress Hook
创建一个可复用的组合式函数,统一配置进度条行为。

// src/hooks/useProgress.ts
import { merge } from 'lodash-es'
import NProgress from 'nprogress'
import type { NProgressOptions } from 'nprogress'

interface ProgressConfig extends NProgressOptions {
show: boolean
}

const DEFAULT_CONFIG: Partial<ProgressConfig> = {
easing: 'ease',
parent: 'body',
show: true,
showSpinner: false,
trickle: true,
minimum: 0.08,
speed: 200,
}

export function useProgress(config: Partial<ProgressConfig> = {}) {
const mergeConfig = merge({}, DEFAULT_CONFIG, config)
NProgress.configure(mergeConfig)

function start() {
    if (!mergeConfig.show) return
    NProgress.start()
}

function done() {
    if (!mergeConfig.show || !NProgress.isStarted()) return
    NProgress.done()
}

return { start, done }
}

该Hook接受自定义配置,与默认配置深度合并后调用NProgress.configure。start和done方法根据show属性决定是否实际执行,避免因环境变量关闭带来的无谓调用。

三、实际应用场景
1. Axios请求拦截器集成
创建通用请求模块,在请求发送前启动进度条,请求完成后结束。

// src/utils/request.ts
import axios from 'axios'
import { useProgress } from '@/hooks/useProgress'

const NProgress = useProgress({ show: import.meta.env.VITE_REQUEST_NPROGRESS !== 'false' })

const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000,
})

instance.interceptors.request.use(
(config) => {
    NProgress.start()
    return config
},
(error) => {
    NProgress.done()
    return Promise.reject(error)
}
)

instance.interceptors.response.use(
(response) => {
    NProgress.done()
    return response
},
(error) => {
    NProgress.done()
    return Promise.reject(error)
}
)

export const request = instance

注意:在请求或响应错误时也要调用done,防止进度条卡住。

2. Vue Router路由守卫集成
在路由切换时自动显示进度条,通过环境变量控制。

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { useProgress } from '@/hooks/useProgress'

const router = createRouter({
history: createWebHistory(),
routes: [],
})

const NProgress = useProgress({ show: import.meta.env.VITE_ROUTER_NPROGRESS !== 'false' })

router.beforeEach((to, from, next) => {
NProgress.start()
next()
})

router.afterEach(() => {
NProgress.done()
})

export default router

若使用了异步路由,确保在路由解析完成后执行done,避免过早结束。

3. 组合式调用示例
在Vue组件的任何异步操作中也可手动调用:

<script setup lang="ts">
import { useProgress } from '@/hooks/useProgress'

const NProgress = useProgress({ show: import.meta.env.VITE_REQUEST_NPROGRESS !== 'false' })

async function loadData() {
NProgress.start()
try {
    await fetch('/api/data')
} finally {
    NProgress.done()
}
}
</script>

务必使用try...finally确保进度条能正确结束。

四、全局样式自定义
NProgress默认提供朴素样式,通常需与项目主题匹配。推荐在全局样式入口文件中覆盖。

创建src/styles/nprogress.scss,定义渐变色进度条:

// src/styles/nprogress.scss
#nprogress .bar {
background: var(--el-color-primary);
height: 3px;
background: linear-gradient(90deg, var(--el-color-primary-light-3) 0%, var(--el-color-primary) 100%);
}

#nprogress .peg {
box-shadow: 0 0 10px var(--el-color-primary);
}

#nprogress .spinner-icon {
border-top-color: var(--el-color-primary);
border-left-color: var(--el-color-primary);
}

然后新建src/styles/index.scss,统一导入:

@use 'nprogress.scss';
@use 'variables.scss';
@use 'transition.scss';
@use 'element-plus/el-table.scss';
@use 'element-plus/el-dialog.scss';
@use 'element-plus/el-dropdown.scss';

body {
font-family: var(--el-font-family);
background-color: var(--el-bg-color-page);
}

最后在main.ts引入:

import { createApp } from 'vue'
import App from './App.vue'
import './styles/index.scss'

const app = createApp(App)
app.mount('#app')

注意:如果有其他样式文件,顺序需处理好。若不需要自定义,也可以直接引入nprogress自带的CSS:

@use 'nprogress/nprogress.css';


五、NProgress配置项详解
配置项支持以下参数(配置通过NProgress.configure传入):


{
easing: 'ease',       // CSS3缓动函数
speed: 200,         // 动画速度(ms)
trickle: true,      // 是否自动递增
trickleSpeed: 200,    // 自动递增速度
minimum: 0.08,      // 起始百分比(0-1)
showSpinner: false,   // 是否显示环形加载动画
parent: 'body',       // 父容器CSS选择器
positionUsing: '',    // 定位方式(可选'absolute','fixed'等)
}

缓动函数推荐使用标准值或自定义贝塞尔曲线,例如:

const EASING_FUNCTIONS = {
linear: 'linear',
ease: 'ease',
smooth: 'cubic-bezier(0.4, 0, 0.2, 1)',
gentle: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
swift: 'cubic-bezier(0.4, 0, 0.6, 1)',
}

可根据项目视觉风格灵活调整。

以上方案已在多个Vue3+TypeScript项目中使用,通过环境变量和配置Hook,能轻松控制进度条的显示与行为,并与Axios、Vue Router深度整合。希望这套封装能帮你快速落地NProgress,提升应用的用户反馈体验。

热心网友1 发表于 2026-6-12 17:10:00

Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫

感谢楼主的详细分享!正好最近在重构一个Vue3项目,进度条这块一直想整合但没找到清晰的方案。你这种通过环境变量控制开关的思路很实用,特别是用lodash-es做深度合并来统一配置,既灵活又干净。请教一下,在路由守卫里如果用了异步路由(比如动态加载组件),有没有遇到过进度条提前结束的情况?我看你提到了“确保在路由解析完成后执行done”,具体是怎么判断解析完成的?另外,组件里手动调用时,如果并发多个请求,start/done的配对会不会有冲突?希望再展开说说,谢谢!

热心网友5 发表于 2026-6-19 11:30:00

Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫

感谢分享,这套方案很完整,尤其是用环境变量控制进度条开关的方式,省去了到处注释代码的麻烦。有个小问题想请教:在请求并发场景下,多个请求同时触发时,`start`和`done`会不会出现进度条提前结束的情况?有没有考虑过用计数器来确保所有请求完成后再关闭?

热心网友5 发表于 2026-6-19 16:10:00

Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫

很详细的实践分享,特别是用`useProgress` Hook封装配置和二次控制`show`属性的思路,避免了进度条在关闭时产生额外调用,这个细节处理得很到位。我比较喜欢用环境变量统一控制开关的方式,比硬编码灵活多了。 另外,你在`request.ts`里同时处理了请求错误和响应错误的`done()`调用,防止进度条卡死——这个其实很多初学者容易忽略,点个赞。路由守卫那边提到异步路由时要确保解析完再`done`,能提醒到实践中的坑,很贴心。 如果后续有需要,或许还可以加一个防抖或延迟启动逻辑:对于极短请求(比如
页: [1]
查看完整版本: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫