如何实现页面加载进度条? 羽霏 ☘︎ 提问于 2026-03-02 12:11:18 阅读 47 优化 我在做项目首页,想加个顶部的加载进度条,但不知道怎么监听整体资源加载进度。 试过用 window.onload,但只能知道什么时候加载完,没法拿到中间的进度。也查了 performance.getEntriesByType('resource'),但感觉拿不到实时百分比啊。 现在用的是 Vue 3 + Vite,有没有办法在路由切换或初始加载时显示一个平滑的进度条?比如像 NProgress 那种,但我想自己实现核心逻辑。 体验优化加载进度 我来解答 赞 12 收藏 分享 生成中... 手机扫码查看 复制链接 生成海报 反馈 发表解答 您需要先 登录/注册 才能发表解答 2 条解答 a'ゞ婧妍 Lv1 实现页面加载进度条这个需求,在 SPA 应用里其实有点 tricky,因为浏览器并没有提供实时的资源加载百分比 API。 先说最直接的方案:用 NProgress 配合 Vue Router,这是最成熟的做法。 // 安装 nprogress // npm install nprogress // router/index.js import { createRouter, createWebHistory } from 'vue-router' import NProgress from 'nprogress' import 'nprogress/nprogress.css' // 配置 NProgress NProgress.configure({ showSpinner: false, // 隐藏右上角那个转圈 minimum: 0.1, // 最小进度值 easing: 'ease', // 动画缓动 speed: 200 // 动画速度 }) const routes = [ // 你的路由配置 ] const router = createRouter({ history: createWebHistory(), routes }) // 路由切换时显示进度条 router.beforeEach((to, from, next) => { NProgress.start() next() }) router.afterEach(() => { NProgress.done() }) export default router 这个方案的好处是简单稳定,NProgress 内部已经帮你做好了平滑动画。 如果你就想自己写核心逻辑,我给你一个手写版本,原理是利用 Vue Router 的导航守卫配合 CSS 动画: /* App.vue 或全局样式 */ .progress-bar { position: fixed; top: 0; left: 0; height: 3px; background: #42b883; /* Vue 绿 */ z-index: 9999; transition: width 0.3s ease-out; } // ProgressBar.vue 组件 import { ref, onMounted, onUnmounted } from 'vue' const progress = ref(0) const isLoading = ref(false) let timer = null // 模拟进度动画 const startProgress = () => { progress.value = 0 isLoading.value = true // 用 setInterval 模拟进度,实际项目中 // 你可以根据资源加载状态调整进度 timer = setInterval(() => { // 模拟非线性进度,越往后越慢 const remaining = 100 - progress.value progress.value += remaining * 0.1 if (progress.value >= 95) { clearInterval(timer) } }, 100) } const done = () => { progress.value = 100 isLoading.value = false // 进度条走完后淡出 setTimeout(() => { progress.value = 0 }, 300) if (timer) { clearInterval(timer) } } // 暴露给外部调用 defineExpose({ startProgress, done }) // 在路由守卫中使用 import { useProgressBar } from '@/components/ProgressBar' router.beforeEach((to, from, next) => { // 调用进度条开始 useProgressBar().startProgress() next() }) router.afterEach(() => { // 模拟资源加载完成,结束进度条 // 实际可以根据 window.onload 或其他信号来判断 setTimeout(() => { useProgressBar().done() }, 500) // 稍微延迟,模拟加载感 }) 说点实际的:为什么很难拿到"真正的"实时百分比? 因为浏览器只给你两个状态:loading 和 done,中间的过程不透明。你用 performance.getEntriesByType('resource') 确实能拿到资源列表,但那是已经加载完成的资源列表,没法实时监听单个资源的下载进度。 除非你用 XMLHttpRequest 的 onprogress 事件或者 fetch 的 body.getReader(),但这对于页面本身的资源加载不适用。 所以业界做法都是"模拟进度",NProgress 那种效果其实是假的进度,只是看起来像那么回事。路由切换时你没法知道下一个页面到底要加载多少资源。 我的建议:直接用 NProgress 就完事了,省心省力。如果你想研究原理,上面那个手写版本够你折腾的了。 回复 点赞 2026-03-10 23:03 上官天朝 Lv1 说实话,真实的资源加载进度基本拿不到精确值,浏览器的 API 限制就在那。实际项目中大多数进度条都是"假装"在加载,用一个定时器模拟进度,真正加载完了直接跳到 100%。 你用 Vue 3 + Vite 的话,分两个场景处理。 第一个场景是路由切换,这个好办,用 Vue Router 的导航守卫配合一个简单的进度条组件就行。写个简单的实现: // progress.js import { ref } from 'vue' const progress = ref(0) let timer = null let isDone = false export function useProgress() { const start = () => { progress.value = 0 isDone = false timer = setInterval(() => { if (isDone) return // 模拟进度,越往后越慢 const increment = Math.random() * 10 * (1 - progress.value / 100) progress.value = Math.min(95, progress.value + increment) }, 100) } const done = () => { isDone = true clearInterval(timer) progress.value = 100 setTimeout(() => { progress.value = 0 }, 300) } return { progress, start, done } } 路由守卫里这样用: // router/index.js import { createRouter, createWebHistory } from 'vue-router' import { useProgress } from './progress' const router = createRouter({ /* 你的路由配置 */ }) const { start, done } = useProgress() router.beforeEach((to, from, next) => { start() next() }) router.afterEach(() => { done() }) 第二个场景是首次加载,这个稍微麻烦点。你可以在 index.html 里内联一段 CSS 和 JS,让进度条在 Vue 还没挂载前就显示。Vite 打包后会有一个入口 JS,用 document.readyState 或者监听 load 事件来判断页面加载完成。 有几个安全点要注意。定时器一定要清理,不然内存泄漏,组件销毁时记得 clearInterval。还有进度条组件尽量用固定定位,别让其他元素影响它。如果你用用户输入来控制进度条(虽然不太可能),要做校验,防止恶意注入。 另外,如果你项目里有异步请求依赖,比如路由组件里要 fetch 数据,可以在组件里手动调 start 和 done,配合 Suspense 或者 loading 状态一起用。 基本上这样就能实现一个类似 NProgress 的效果了,代码也不多,自己掌控也更灵活。 回复 点赞 2026-03-02 12:15 加载更多 相关推荐 1 回答 41 浏览 全局加载遮罩怎么实现才不会影响页面滚动? 我在做后台管理系统,想加个全局加载状态,比如请求数据时整个页面蒙一层。但试了下直接用 fixed 定位的 div 遮罩,发现页面还能滚动,而且遮罩层有时候会被某些弹窗盖住,层级也不对。 我现在的写法大... 技术焕玲 交互 2026-03-31 12:31:14 2 回答 48 浏览 为什么我的CSS旋转加载动画在页面加载时会突然卡顿一下? 我在做一个简单的旋转加载组件,用CSS3动画实现图标旋转。但每次页面加载时,图标会先显示0.5秒的静态状态,然后才开始旋转。我尝试过把animation-delay设为0和调整will-change属... 司马翌岍 组件 2026-02-16 18:11:24 2 回答 70 浏览 React中如何实现渐进增强的图片懒加载兼容旧浏览器? 我在用React做图片懒加载时遇到了问题,用IntersectionObserver实现的方案在IE11完全失效,基础图片都不显示了。我试过在组件里这样写: function LazyImage({ ... ლ雨诺 优化 2026-02-12 12:21:26 2 回答 51 浏览 Vue项目中使用IntersectionObserver实现加载进度条导致滚动卡顿怎么办? 在Vue项目里想用IntersectionObserver检测关键资源加载进度,然后发现滚动时页面卡顿,特别是资源较多时更明显。我尝试给每个资源元素添加了观察器,然后在回调里计算总进度: const ... 设计师硕辰 优化 2026-02-12 05:37:22 2 回答 58 浏览 React骨架屏组件在动态数据加载时如何保持布局一致性? 我在用Skeleton骨架屏实现列表加载效果时遇到了问题。当异步数据加载完成,真实内容替换骨架屏时,页面布局会突然跳动一下。 我已经尝试给Skeleton和真实内容都设置了相同的固定高度,但实际渲染后... 司马士媛 组件 2026-02-10 21:52:29 2 回答 84 浏览 预获取(prefetch)在单页应用中如何正确使用?为什么我的页面加载反而变慢了? 我在开发一个Vue单页应用时,尝试给下一页的图片列表页面添加预获取。按文档写了类似下面的代码,但发现首页加载反而变慢了,控制台显示预加载了大量图片资源。这是不是预获取用错了? <template... 西门智颖 优化 2026-01-27 23:52:24 2 回答 72 浏览 React中如何实现移动端图片列表的按需加载? 在开发移动端图片列表时,我尝试用Intersection Observer实现按需加载,但发现滚动到可视区时图片没及时加载。我按教程写了代码,但控制台报"Uncaught TypeError: obs... UE丶艳艳 移动 2026-01-27 20:46:25 2 回答 154 浏览 预加载图片时如何避免内存占用过高导致页面卡顿? 在做移动端图片列表页时,用Intersection Observer做预加载,但发现滚动时内存飙升,页面偶尔卡顿。我设置了同时加载5张临近图片,但测试发现已滑出屏幕的图片元素并未被回收... 尝试过在... 极客爱香 移动 2026-01-26 20:51:26 1 回答 54 浏览 首屏加载时如何正确实现 Loading 占位避免布局抖动? 我在做首页优化,数据还没回来的时候想用 skeleton loading 占位,但每次数据加载完 DOM 结构一变,页面就会“跳一下”,体验很差。我试过给容器写死高度,但不同设备下内容高度不一样,不太... 设计师小倩 优化 2026-03-31 08:49:14 1 回答 41 浏览 占位图加载时怎么避免页面跳动? 我在做图片懒加载,用了一个灰色背景当占位图,但图片加载完后容器高度变了,页面会突然跳一下,体验很不好。试过给img加固定宽高,但响应式布局下又不合适。 现在用的CSS是这样: .image-place... 付敏 ☘︎ 优化 2026-03-29 18:16:12
先说最直接的方案:用 NProgress 配合 Vue Router,这是最成熟的做法。
这个方案的好处是简单稳定,NProgress 内部已经帮你做好了平滑动画。
如果你就想自己写核心逻辑,我给你一个手写版本,原理是利用 Vue Router 的导航守卫配合 CSS 动画:
说点实际的:为什么很难拿到"真正的"实时百分比?
因为浏览器只给你两个状态:loading 和 done,中间的过程不透明。你用 performance.getEntriesByType('resource') 确实能拿到资源列表,但那是已经加载完成的资源列表,没法实时监听单个资源的下载进度。
除非你用 XMLHttpRequest 的 onprogress 事件或者 fetch 的 body.getReader(),但这对于页面本身的资源加载不适用。
所以业界做法都是"模拟进度",NProgress 那种效果其实是假的进度,只是看起来像那么回事。路由切换时你没法知道下一个页面到底要加载多少资源。
我的建议:直接用 NProgress 就完事了,省心省力。如果你想研究原理,上面那个手写版本够你折腾的了。
你用 Vue 3 + Vite 的话,分两个场景处理。
第一个场景是路由切换,这个好办,用 Vue Router 的导航守卫配合一个简单的进度条组件就行。写个简单的实现:
路由守卫里这样用:
第二个场景是首次加载,这个稍微麻烦点。你可以在 index.html 里内联一段 CSS 和 JS,让进度条在 Vue 还没挂载前就显示。Vite 打包后会有一个入口 JS,用 document.readyState 或者监听 load 事件来判断页面加载完成。
有几个安全点要注意。定时器一定要清理,不然内存泄漏,组件销毁时记得 clearInterval。还有进度条组件尽量用固定定位,别让其他元素影响它。如果你用用户输入来控制进度条(虽然不太可能),要做校验,防止恶意注入。
另外,如果你项目里有异步请求依赖,比如路由组件里要 fetch 数据,可以在组件里手动调 start 和 done,配合 Suspense 或者 loading 状态一起用。
基本上这样就能实现一个类似 NProgress 的效果了,代码也不多,自己掌控也更灵活。