React 中的路由懒加载入门:提升首屏性能的第一步
在现代前端开发中,React 应用的体积越来越大 ,尤其是随着功能的不断扩展,首屏加载时间也随之增加。为了优化用户体验和提高应用性能,路由懒加载(Lazy Loading Routes) 成为了一种非常重要的技术手段。
本文将带你深入了解如何在 React 项目中实现路由懒加载,并结合 React.lazy
、Suspense
和 React Router
实现高效的代码分割与按需加载。
一、什么是路由懒加载?
路由懒加载指的是在用户访问某个路由时,才动态加载对应的组件资源。这种方式可以避免一次性加载整个应用的所有代码,而是根据需要分块加载,从而显著减少初始加载时间。
-
没有使用路由懒加载的情况
根组件组件
jsximport { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Home from './pages/Home' import About from './pages/About' import Login from './pages/Login' import Navigation from './components/Navigation' import './App.css' function App() { return ( <> <Router> <Navigation /> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/login" element={<Login />} /> </Routes> </Router> </> ) } export default App
Home组件
jsxconsole.log('加载Home组件') export default function Home() { console.log('Home') return ( <div> <h1>Home</h1> <p>这是Home页面</p> </div> ); }
About组件
jsxconsole.log('加载About组件') export default function About() { console.log("About组件"); return ( <div> <h1>About</h1> <p>这是About页面</p> </div> ); }
Login组件
jsxconsole.log('加载Login组件') export default function Login() { console.log('Login') return ( <div> <h1>登录</h1> </div> ); }
输出情况
加载Home组件 加载About组件 加载Login组件 Home
这是因为当使用import导入组件时,浏览器或打包工具(如 Webpack、Vite)会加载并执行整个
.jxs
文件(不会执行组件函数)。所以这些组件都加载并执行了,若应用中存在多个路由页面时,如果所有组件都在首页一次性加载,会导致页面加载速度变慢,影响用户体验。 -
使用路由懒加载的情况
jsximport { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Navigation from "./components/Navigation"; import "./App.css"; import { lazy, Suspense } from "react"; function App() { const Home = lazy(() => import("./pages/Home")); const About = lazy(() => import("./pages/About")); const Login = lazy(() => import("./pages/Login")); return ( <> <Router> <Navigation /> <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/login" element={<Login />} /> </Routes> </Suspense> </Router> </> ); } export default App;
输出情况
加载Home组件 Home
从输出情况可以看出 除了Home组件加载了,其他组件都没有加载
二、React 中实现懒加载的核心 API
-
React.lazy
React.lazy
是 React 提供的一个用于延迟加载组件的函数。它介绍一个函数作为参数,这个函数返回import()
动态导入。lazy返回的是一个特殊的 React 组件,这个组件在首次渲染时才会真正加载。jsximport { lazy } from "react"; const Home = lazy(() => import('./Home'));
-
<Suspense>
由于懒加载组件是异步加载的,在加载完成之前,我们需要显示一个"加载中"的状态 ,为用户提供良好的体验。
<Suspense>
就是用来包裹这些懒加载组件并提供加载状态的组件。jsximport React, { Suspense } from 'react'; function App() { return ( <Suspense fallback="Loading..."> <Home /> </Suspense> ); }
-
执行逻辑
首次渲染 时React 会执行
() => import('./Home')
,然后开始异步加载Home
的代码,在加载期间显示<Suspense>
的fallback
UI。加载完成后 动态导入的组件会替换这个占位组件并渲染实际的SomeComponent
三、在 React Router 中实现路由懒加载
如果你使用的是 React Router v6+ ,可以通过组合 React.lazy
+ Suspense
来实现懒加载路由。
示例:基本结构
jsx
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// 懒加载页面组件
const Home = React.lazy(() => import('./pages/Home'));
const About = React.lazy(() => import('./pages/About'));
const Contact = React.lazy(() => import('./pages/Contact'));
function App() {
return (
<Router>
<Suspense fallback="Loading...">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</Router>
);
}
export default App;
这样,每个页面组件都会在用户访问对应路径时才加载,而不是一开始就全部加载。
四、增强体验:添加加载状态与错误处理
-
自定义加载组件
你可以自定义一个通用的加载提示组件:
jsxfunction Loading() { return <div className="loading">加载中,请稍候...</div>; }
然后在
Suspense
中使用:jsx<Suspense fallback={<Loading />}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Suspense>
-
错误边界(Error Boundary)
懒加载过程中可能会出现网络问题或其他错误,建议使用错误边界来捕获异常,避免页面崩溃。
jsxclass ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } render() { if (this.state.hasError) { return <div>页面加载出错,请刷新重试。</div>; } return this.props.children; } }
结合使用:
jsx<ErrorBoundary> <Suspense fallback={<Loading />}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Suspense> </ErrorBoundary>