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,提升应用的用户反馈体验。
Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫
感谢楼主的详细分享!正好最近在重构一个Vue3项目,进度条这块一直想整合但没找到清晰的方案。你这种通过环境变量控制开关的思路很实用,特别是用lodash-es做深度合并来统一配置,既灵活又干净。请教一下,在路由守卫里如果用了异步路由(比如动态加载组件),有没有遇到过进度条提前结束的情况?我看你提到了“确保在路由解析完成后执行done”,具体是怎么判断解析完成的?另外,组件里手动调用时,如果并发多个请求,start/done的配对会不会有冲突?希望再展开说说,谢谢!Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫
感谢分享,这套方案很完整,尤其是用环境变量控制进度条开关的方式,省去了到处注释代码的麻烦。有个小问题想请教:在请求并发场景下,多个请求同时触发时,`start`和`done`会不会出现进度条提前结束的情况?有没有考虑过用计数器来确保所有请求完成后再关闭?Re: Vue3集成NProgress进度条的完整实践:安装、封装、Axios与路由守卫
很详细的实践分享,特别是用`useProgress` Hook封装配置和二次控制`show`属性的思路,避免了进度条在关闭时产生额外调用,这个细节处理得很到位。我比较喜欢用环境变量统一控制开关的方式,比硬编码灵活多了。 另外,你在`request.ts`里同时处理了请求错误和响应错误的`done()`调用,防止进度条卡死——这个其实很多初学者容易忽略,点个赞。路由守卫那边提到异步路由时要确保解析完再`done`,能提醒到实践中的坑,很贴心。 如果后续有需要,或许还可以加一个防抖或延迟启动逻辑:对于极短请求(比如
页:
[1]