NProgress 核心知识汇总(Vue3 + CRM 加载进度条实战)
·
NProgress 是轻量级、高颜值的进度条库(仅 2KB),在 CRM 系统中主要用于全局接口请求加载、路由跳转加载、大文件上传 / 下载进度等场景,能直观反馈操作进度,提升用户体验。下面从「核心集成→Vue3 实战配置→CRM 高频场景→定制化」全维度讲解。
一、核心定位(CRM 为什么需要 NProgress?)
CRM 系统的核心痛点:
- 无感加载:接口请求 / 路由跳转耗时较长时,用户不知道是否在加载,易重复操作;
- 进度反馈:大文件上传(如合同、物料图片)、批量数据导出需展示实时进度;
- 全局统一:所有加载场景用统一的进度条样式,保持视觉一致性;
- 轻量无侵入:体积小,接入成本低,不影响核心业务逻辑。
NProgress 核心优势:
- 纯 JS 实现,无依赖,支持 Vue/React/ 原生 JS;
- 可自定义颜色、速度、位置、动画;
- 支持手动控制进度(0-100%),适配异步操作;
- 样式简洁,可快速融入 CRM 品牌风格。
二、快速集成(Vue3 + Vite 项目)
1. 安装依赖
bash
运行
# 核心库
npm install nprogress -S
# TS 类型(可选)
npm install @types/nprogress -D
# 引入样式(必须!)
import 'nprogress/nprogress.css';
2. 封装通用进度条工具(CRM 首选)
封装统一的 NProgress 工具类,避免重复调用,同时统一配置全局样式:
typescript
运行
// src/utils/nprogress.ts
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
// NProgress 全局配置
NProgress.configure({
easing: 'ease', // 动画方式
speed: 500, // 进度条速度(ms)
showSpinner: false, // 隐藏加载转圈图标(CRM 更简洁)
trickleSpeed: 80, // 自动递增速度(ms)
minimum: 0.1, // 最小进度值(避免进度条瞬间消失)
});
/**
* 启动进度条
* @param percentage 初始进度(可选,默认自动递增)
*/
export const startProgress = (percentage?: number) => {
NProgress.start();
if (percentage !== undefined && percentage >= 0 && percentage <= 1) {
NProgress.set(percentage);
}
};
/**
* 设置进度条进度
* @param percentage 进度值(0-1)
*/
export const setProgress = (percentage: number) => {
if (percentage < 0) percentage = 0;
if (percentage > 1) percentage = 1;
NProgress.set(percentage);
};
/**
* 结束进度条(自动完成到100%)
*/
export const doneProgress = () => {
NProgress.done();
};
/**
* 中断进度条(立即隐藏)
*/
export const stopProgress = () => {
NProgress.done(true);
};
// 导出原始 NProgress 实例(自定义扩展用)
export default NProgress;
3. 全局样式定制(适配 CRM 品牌)
修改 NProgress 默认样式,贴合 CRM 主题色(如蓝色):
css
/* src/styles/nprogress.css */
/* 进度条背景 */
#nprogress .bar {
background: #1890ff !important; /* CRM 主色 */
height: 3px !important; /* 进度条高度 */
}
/* 进度条阴影(可选) */
#nprogress .peg {
box-shadow: 0 0 10px #1890ff, 0 0 5px #1890ff !important;
}
/* 隐藏默认转圈图标(已在配置中关闭,此处兜底) */
#nprogress .spinner {
display: none !important;
}
在 main.ts 引入自定义样式(覆盖默认样式):
typescript
运行
import { createApp } from 'vue';
import App from './App.vue';
// 先引入默认样式,再引入自定义样式
import 'nprogress/nprogress.css';
import '@/styles/nprogress.css';
const app = createApp(App);
app.mount('#app');
三、CRM 实战场景(核心用法)
场景 1:路由跳转进度条(全局)
CRM 路由跳转(如从客户列表到订单详情)时展示进度条,适配异步路由加载:
typescript
运行
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { startProgress, doneProgress } from '@/utils/nprogress';
import routes from './routes';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
// 路由开始跳转时启动进度条
router.beforeEach((to, from, next) => {
startProgress();
next();
});
// 路由跳转完成后结束进度条
router.afterEach(() => {
// 延迟结束(避免进度条闪一下,提升体验)
setTimeout(() => {
doneProgress();
}, 300);
});
// 路由跳转失败时中断进度条
router.onError(() => {
stopProgress();
});
export default router;
场景 2:全局接口请求进度条(Axios 拦截器)
CRM 所有接口请求统一展示进度条,支持批量请求合并进度:
typescript
运行
// src/utils/axios.ts
import axios from 'axios';
import { startProgress, setProgress, doneProgress, stopProgress } from '@/utils/nprogress';
import { ElMessage } from 'element-plus';
// 记录当前请求数(避免多个请求重复启动/结束进度条)
let requestCount = 0;
// 创建 Axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 10000,
});
// 请求拦截器:启动进度条
service.interceptors.request.use(
(config) => {
requestCount++;
// 第一个请求启动进度条
if (requestCount === 1) {
startProgress();
}
// 自定义配置:是否显示进度条(默认显示)
if (config.headers?.hideProgress) {
return config;
}
return config;
},
(error) => {
requestCount--;
if (requestCount === 0) {
stopProgress();
}
ElMessage.error('请求异常:' + error.message);
return Promise.reject(error);
}
);
// 响应拦截器:结束进度条
service.interceptors.response.use(
(response) => {
requestCount--;
// 最后一个请求结束进度条
if (requestCount === 0) {
doneProgress();
}
return response.data;
},
(error) => {
requestCount--;
if (requestCount === 0) {
stopProgress();
}
ElMessage.error('请求失败:' + (error.response?.data?.msg || error.message));
return Promise.reject(error);
}
);
// 扩展:支持手动设置请求进度(如大文件上传)
service.interceptors.request.use((config) => {
// 如果配置了 onUploadProgress,则手动更新进度条
if (config.onUploadProgress) {
const originalOnUploadProgress = config.onUploadProgress;
config.onUploadProgress = (progressEvent) => {
const percent = progressEvent.loaded / progressEvent.total;
setProgress(percent);
originalOnUploadProgress(progressEvent);
};
}
return config;
});
export default service;
场景 3:大文件上传 / 下载进度(手动控制)
CRM 上传合同 / 物料图片、下载批量数据时,展示实时进度:
vue
<template>
<div class="file-upload">
<input
type="file"
accept=".pdf,.jpg,.png"
@change="handleFileUpload"
/>
<!-- 进度条展示(可选:复用 NProgress 或单独展示) -->
<div class="upload-progress" v-if="uploading">
<span>上传进度:{{ uploadPercent }}%</span>
<el-progress :percentage="uploadPercent" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { startProgress, setProgress, doneProgress, stopProgress } from '@/utils/nprogress';
import { uploadToOss } from '@/utils/oss';
import { ElMessage } from 'element-plus';
const uploading = ref(false);
const uploadPercent = ref(0);
// 大文件上传(带进度)
const handleFileUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
uploading.value = true;
uploadPercent.value = 0;
// 启动进度条
startProgress(0);
try {
// 调用 OSS 上传,传入进度回调
await uploadToOss(
file,
'crm/contract/',
(percent) => {
uploadPercent.value = percent;
// 手动更新 NProgress 进度
setProgress(percent / 100);
}
);
// 完成进度条
doneProgress();
ElMessage.success('文件上传成功');
} catch (err) {
// 中断进度条
stopProgress();
ElMessage.error('文件上传失败:' + (err as Error).message);
} finally {
uploading.value = false;
uploadPercent.value = 0;
e.target.value = '';
}
};
</script>
<style scoped>
.file-upload {
display: flex;
flex-direction: column;
gap: 12px;
}
.upload-progress {
width: 100%;
gap: 8px;
display: flex;
flex-direction: column;
}
</style>
场景 4:批量数据处理进度(手动控制)
CRM 批量导入客户、批量更新订单时,展示处理进度:
vue
<template>
<button
class="btn-crm"
@click="handleBatchImport"
:disabled="processing"
>
批量导入客户
</button>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { startProgress, setProgress, doneProgress } from '@/utils/nprogress';
import { ElMessage } from 'element-plus';
const processing = ref(false);
// 批量导入客户(模拟分步处理)
const handleBatchImport = async () => {
processing.value = true;
startProgress(0);
try {
// 步骤1:读取文件(20%)
await new Promise(resolve => setTimeout(resolve, 500));
setProgress(0.2);
// 步骤2:解析数据(50%)
await new Promise(resolve => setTimeout(resolve, 800));
setProgress(0.5);
// 步骤3:验证数据(80%)
await new Promise(resolve => setTimeout(resolve, 600));
setProgress(0.8);
// 步骤4:提交数据(100%)
await new Promise(resolve => setTimeout(resolve, 700));
doneProgress();
ElMessage.success('批量导入客户成功');
} catch (err) {
stopProgress();
ElMessage.error('批量导入失败:' + (err as Error).message);
} finally {
processing.value = false;
}
};
</script>
四、核心避坑点
1. 进度条重复启动 / 结束
- 问题:多个接口同时请求时,进度条刚结束又立即启动,闪跳严重;
- 解决方案:
-
- 用
requestCount计数,仅第一个请求启动、最后一个请求结束; - 路由跳转进度条延迟结束(如 300ms),避免闪跳。
- 用
2. 进度条无法隐藏
- 问题:请求失败 / 中断后,进度条卡在中间不消失;
- 解决方案:
-
- 异常场景调用
stopProgress()(NProgress.done(true)),立即隐藏; - 给所有异步操作加
finally,兜底重置进度条。
- 异常场景调用
3. 样式覆盖不生效
- 问题:自定义的进度条颜色 / 高度不生效;
- 解决方案:
-
- 自定义样式文件放在默认样式之后引入;
- 样式加
!important强制覆盖; - 检查 CSS 选择器优先级(
#nprogress .bar优先级高于.bar)。
4. 移动端进度条适配问题
- 问题:移动端进度条显示不全、位置偏移;
- 解决方案:
-
- 进度条高度设为 2-3px(适配移动端);
- 禁用
showSpinner,避免移动端布局错乱; - 配置
minimum: 0.05,移动端更灵敏。
5. 进度条速度过快 / 过慢
- 问题:短时间请求进度条刚显示就消失,体验差;
- 解决方案:
-
- 路由跳转进度条延迟结束(300-500ms);
- 调整
speed(500ms)和trickleSpeed(80ms); - 手动设置初始进度(如
startProgress(0.1))。
五、定制化扩展(CRM 进阶)
1. 多主题进度条(适配深色模式)
根据 CRM 主题切换进度条颜色:
typescript
运行
// src/utils/nprogress.ts
/**
* 切换进度条主题
* @param theme 主题(light/dark)
*/
export const switchProgressTheme = (theme: 'light' | 'dark') => {
const bar = document.getElementById('nprogress')?.querySelector('.bar');
if (!bar) return;
if (theme === 'dark') {
bar.style.background = '#40a9ff'; // 深色模式主色
bar.style.boxShadow = '0 0 10px #40a9ff, 0 0 5px #40a9ff';
} else {
bar.style.background = '#1890ff'; // 浅色模式主色
bar.style.boxShadow = '0 0 10px #1890ff, 0 0 5px #1890ff';
}
};
// 主题切换时调用
// watch(theme, (val) => {
// switchProgressTheme(val);
// });
2. 进度条回调(自定义操作)
监听进度条状态变化,执行自定义逻辑:
typescript
运行
// 监听进度条开始
NProgress.on('start', () => {
console.log('进度条启动');
// 禁用页面滚动(可选)
document.body.style.overflow = 'hidden';
});
// 监听进度条结束
NProgress.on('done', () => {
console.log('进度条结束');
// 恢复页面滚动
document.body.style.overflow = 'auto';
});
六、总结(CRM 开发最佳实践)
- 封装统一工具:将 NProgress 封装为全局工具函数,统一配置和样式,避免重复代码;
- 场景适配:
-
- 路由跳转:全局路由拦截,延迟结束进度条;
- 接口请求:Axios 拦截器计数,批量请求合并进度;
- 大文件 / 批量操作:手动控制进度,展示实时百分比;
- 体验优化:
-
- 避免进度条闪跳:计数控制启动 / 结束,延迟结束;
- 异常兜底:所有异步操作加
finally,中断进度条; - 样式定制:贴合 CRM 品牌色,移动端适配高度;
- 避坑核心:
-
- 用请求计数解决重复启动 / 结束问题;
- 自定义样式加
!important强制覆盖; - 异常场景调用
stopProgress()立即隐藏。
NProgress 是 Vue3 + CRM 进度反馈的 “最优解”,接入成本低、体验好,只需封装一次即可覆盖所有加载场景;结合路由、Axios 拦截器,能实现全局统一的进度反馈,大幅提升 CRM 系统的用户体验。
更多推荐

所有评论(0)