React中类似YouTube/GitHub顶部水平加载进度条实现方法咨询
嘿,这个问题抓得很准!那种像YouTube、GitHub一样的顶部加载进度条,核心可不是花架子——它是真的和实际的网络请求、页面加载进度挂钩的,网络慢的时候自然会停在对应阶段。我给你分享两种最常用的实现思路,从快速上手到自定义都有:
一、路由切换场景:配合React Router实现
大部分时候这种进度条是在页面路由跳转时触发的(比如切换页面加载数据),这里用轻量库nprogress配合React Router来快速实现,省心又好用:
步骤1:安装依赖
npm install nprogress react-router-dom
步骤2:封装进度条组件
新建一个ProgressBar.jsx,用来监听路由变化并控制进度条:
import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; // 别忘了引入默认样式 const ProgressBar = () => { const location = useLocation(); useEffect(() => { // 路由开始切换时启动进度条 NProgress.start(); // 如果你想让进度条和页面数据加载联动,这里不要用固定延迟,而是在页面组件里手动控制进度 const finishTimer = setTimeout(() => { NProgress.done(); }, 300); // 路由取消或组件卸载时,确保进度条结束 return () => { clearTimeout(finishTimer); NProgress.done(); }; }, [location]); return null; // 这个组件只做逻辑处理,不需要渲染DOM }; export default ProgressBar;
步骤3:全局引入组件
在你的根组件App.jsx里,把进度条组件放在路由外层:
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import ProgressBar from './ProgressBar'; import Home from './pages/Home'; import About from './pages/About'; function App() { return ( <Router> <ProgressBar /> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Router> ); }
进阶:和实际数据加载联动
如果不想用固定延迟,而是让进度条跟着数据请求走,比如在Home页面里:
import { useEffect, useState } from 'react'; import NProgress from 'nprogress'; const Home = () => { const [data, setData] = useState(null); useEffect(() => { const fetchData = async () => { try { NProgress.set(0.3); // 手动设置进度:请求开始 const res = await fetch('/api/home-data'); NProgress.set(0.7); // 请求已响应,正在解析数据 const result = await res.json(); setData(result); NProgress.done(); // 数据加载完成,结束进度条 } catch (err) { NProgress.done(); // 出错也要结束进度条 console.error('数据加载失败:', err); } }; fetchData(); }, []); return ( <div className="home-page"> {data ? <div>{data.content}</div> : <p>加载中...</p>} </div> ); };
二、全局API请求场景:拦截所有网络请求
如果要让进度条监听所有的API请求(不管是不是路由切换触发的),可以用axios的拦截器来实现全局控制:
步骤1:安装依赖
npm install axios nprogress
步骤2:封装axios实例
新建api.js,在里面配置请求/响应拦截器:
import axios from 'axios'; import NProgress from 'nprogress'; // 创建axios实例 const api = axios.create({ baseURL: '/api', // 你的API基础路径 }); // 请求拦截器:发起请求时启动进度条 api.interceptors.request.use( (config) => { NProgress.start(); return config; }, (error) => { NProgress.done(); // 请求失败也要结束进度条 return Promise.reject(error); } ); // 响应拦截器:请求完成(成功/失败)时结束进度条 api.interceptors.response.use( (response) => { NProgress.done(); return response; }, (error) => { NProgress.done(); return Promise.reject(error); } ); export default api;
之后所有的API请求都用这个api实例发起,进度条就会自动跟着所有请求的状态走——网络慢的时候,请求没完成,进度条就会停在当前状态,直到请求结束。
三、完全自定义实现(不用第三方库)
如果你不想依赖第三方库,也可以自己手写一个进度条组件,灵活性更高:
1. 自定义进度条组件
新建CustomProgressBar.jsx:
import { useState, useEffect } from 'react'; import './CustomProgressBar.css'; const CustomProgressBar = ({ isLoading, progress }) => { const [currentProgress, setCurrentProgress] = useState(0); useEffect(() => { if (isLoading) { // 模拟进度增长,也可以直接使用传入的progress值 const progressTimer = setInterval(() => { setCurrentProgress((prev) => { if (prev >= progress) return prev; return prev + 5; }); }, 100); return () => clearInterval(progressTimer); } else { // 加载完成后,让进度条走到100%再隐藏 setCurrentProgress(100); const hideTimer = setTimeout(() => { setCurrentProgress(0); }, 300); return () => clearTimeout(hideTimer); } }, [isLoading, progress]); return ( <div className="progress-bar-container"> <div className="progress-bar" style={{ width: `${currentProgress}%` }} /> </div> ); }; export default CustomProgressBar;
2. 编写样式文件CustomProgressBar.css
.progress-bar-container { position: fixed; top: 0; left: 0; width: 100%; height: 3px; background-color: #f5f5f5; z-index: 9999; /* 确保在最顶层 */ } .progress-bar { height: 100%; background-color: #2196f3; transition: width 0.2s ease-in-out; }
3. 使用自定义进度条
在需要的地方引入并控制状态,比如路由监听组件:
import { useState, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import CustomProgressBar from './CustomProgressBar'; const ProgressWrapper = () => { const [isLoading, setIsLoading] = useState(false); const [progress, setProgress] = useState(0); const location = useLocation(); useEffect(() => { setIsLoading(true); setProgress(0); // 模拟实际的数据请求流程 const fetchPageData = async () => { try { setProgress(30); await new Promise(resolve => setTimeout(resolve, 1200)); // 模拟网络请求延迟 setProgress(70); await new Promise(resolve => setTimeout(resolve, 800)); setProgress(100); setIsLoading(false); } catch (err) { setIsLoading(false); console.error('加载失败:', err); } }; fetchPageData(); }, [location]); return <CustomProgressBar isLoading={isLoading} progress={progress} />; };
核心总结一下:这种进度条的关键是让进度状态和实际的加载/请求生命周期绑定,而不是单纯播放循环动画。网络慢的时候,只要请求没完成,进度就会停在对应阶段,直到请求结束才会完成。
内容的提问来源于stack exchange,提问作者cross19xx
相关产品推荐
相关产品推荐

