在现代的前端开发中,路由懒加载(Lazy Loading)已经成为提升应用性能的重要手段之一。本文将结合完整的代码示例,带你一步步了解如何在 React 项目中实现路由懒加载,并探讨其背后的原理与优势。
🧱 一、React 路由基础结构搭建
首先,我们从一个简单的 React 应用开始。假设你已经使用 npm init vite 创建了一个 React 项目,并安装了 react-router-dom:
npm install react-router-dom
1. 引入必要的模块
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom'
BrowserRouter as Router:
BrowserRouter是react-router-dom提供的一个组件,它使用HTML5的history API来保持UI同步于URL。简单来说,它允许你通过改变浏览器地址栏中的URL来导航应用的不同部分,而无需重新加载整个页面。- 使用
as Router给BrowserRouter起了一个别名Router。这样做可以简化代码,使得每次使用时只需写Router而不是更长的BrowserRouter。
Routes:
Routes是一个用于包裹一组Route组件的容器。它会遍历其所有子Route元素,并仅渲染第一个与当前 URL 匹配的Route子元素。这有助于组织和管理多个路由规则。
Route:
Route组件是定义路由规则的关键。每个Route都指定了一些属性,比如path(路径模式)和element(要渲染的组件)。当应用程序的 URL 与Route的path属性匹配时,Route将渲染指定的组件。
2. 基础 App 组件结构
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
)
}
这里我们引入了两个页面组件:Home 和 About。
3.创建子路由
创建对应的pages文件夹,并在pages文件夹下创建与页面相关的网页内容,声明index.jsx文件,如果在此处声明的文件名不为index.jsx,则在根组件当中引入的路径代码也要有所改变。
// Home 页面
const Home = () => {
return (
<>
<h1>Home</h1>
</>
)
}
export default Home;
// About 页面
const About = () => {
return (
<>
<h1>About</h1>
</>
)
}
export default About;
🧭 二、封装导航栏组件 Navigation
通过将导航栏封装成独立的组件(如 Navigation),你可以在应用中的多个地方重复使用它。例如,如果未来你需要在不同的页面或者布局中使用相同的导航结构,只需导入并使用这个组件即可,而无需重新编写或复制粘贴代码。
1.导航栏封装成components在Navigation中引入路由
// components/Navigation.jsx
import { Link } from 'react-router-dom'
const Navigation = () => {
return (
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
)
}
export default Navigation
2.在App.jsx中引入并且渲染到页面上
import Navigation from './components/Navigation'
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
3.导航栏为何放在Router中
1. 提供全局导航:
- 导航栏通常包含一些链接(通过
Link组件实现),允许用户在不同页面之间切换。这些链接需要能够访问路由上下文来执行导航操作。由于Router提供了这个上下文,所以导航栏应该放在Router内部。
2. 不在特定路由中渲染:
- 导航栏是一个全局性的 UI 元素,不应该受限于任何特定的路由。如果你将导航栏放在
Routes或者某个具体的Route中,那么它只会在这个特定的路由下显示,而不会在整个应用中都可用。这显然对于我们当初将导航栏抽离封装成组件的初衷是不符合的。
3. 保持逻辑清晰:
- 将导航栏置于
Router下但位于Routes外部,可以保持代码逻辑清晰。Router负责提供必要的路由功能给整个应用,包括导航栏;而Routes则专注于根据 URL 渲染正确的页面内容。这样分工明确,便于维护和扩展。
4.根组件源码
import { useState } from 'react'
import './App.css'
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom'
import Navigation from './components/Navigation'
import Home from './pages/Home'
import About from './pages/About'
function App() {
return (
<>
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
</>
)
}
export default App
可以看到上述代码当中<Navigation />在这里有什么问题呢?通过对Home和About设置打印结果可以看到,每次当首页渲染的时候,About页面也会随之渲染。当Home和About的页面内容巨大的时候,页面加载的速度就会变的十分缓慢,为此我们需要新的工具来解决该问题!
⏳ 三、为什么需要路由懒加载?
默认情况下,React 在初始化时就会同步加载所有页面组件。例如上面的代码中,即使用户只访问了 /Home,也会同时加载 Home 和 About 页面的内容。
这会导致:
- 首屏加载变慢
- 资源浪费(未访问的页面也加载了)
特别是在大型项目中,这种问题尤为明显。因此我们需要引入 懒加载机制。
四、React 懒加载核心 API:lazy() + Suspense
React 提供了两个关键 API 来实现懒加载:
React.lazy():用于动态导入组件Suspense:用于处理异步加载过程中的状态(如 loading)
import {
useState,
lazy,
Suspense
} from 'react'
在根组件当中引入lazy()和Suspense两个关键API
1. 修改引入方式
将原本的静态导入改为动态导入:
const Home = React.lazy(() => import('./pages/Home'))
const About = React.lazy(() => import('./pages/About'))
const NotFound = React.lazy(()=> import('./pages/NotFound'))
2. 使用 Suspense 包裹路由
Suspense的作用是什么,在没有把相应的页面渲染出来的时候,可以用Suspense临时显示一些整体页面的框架内容。Suspense也是一个组件,这个组件的用法可以看看会包着所用的routes,让我们看到下面的代码
import React, { Suspense } from 'react'
function App() {
return (
<Router>
<Navigation />
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
)
}
3.添加404页面兜底
这样,只有当用户访问到对应路径时,才会加载对应的组件。并在加载过程中,页面上会显示 Loading...。
当我们访问的url地址是错误的时候,页面上会跳转到NotFound路由地址页面上。
function App() {
return (
<Router>
<Navigation />
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</Router>
)
}
当我们修改了代码之后根组件的源码如下:
import {
useState,
lazy,
Suspense
} from 'react'
import './App.css'
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom'
import Navigation from './components/Navigation'
// 函数 路由 -> Route
// 懒加载
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const NotFound = lazy(() => import('./pages/NotFound'))
// import Home from './pages/Home'
// import About from './pages/About'
function App() {
return (
<>
<Router>
<Navigation />
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</Router>
</>
)
}
export default App
📈 五、懒加载带来的性能提升
通过懒加载,可以带来以下显著的好处:
| 优势 | 描述 |
|---|---|
| ✅ 首屏加载更快 | 只加载当前页面所需内容,减少初始请求体积 |
| ✅ 减少内存占用 | 未访问页面不会被加载进内存 |
| ✅ 更好的用户体验 | 用户无需等待整个应用加载完成即可开始交互 |
| ✅ 支持渐进式加载 | 复杂页面可以逐步加载,提升感知性能 |
六、懒加载完整流程
es6 加载并执行太多的非必要组件,影响首页的加载速度,特别是页面多的时候。因此为解决路由的懒加载,让我们来看看下面实现懒加载的流程。
- 用户访问首页 → 只加载
Home组件 - 点击“关于”链接 → 触发
/about路由匹配 - React.lazy() 动态导入
About组件 - 加载期间显示
fallback内容(如 loading) - 组件加载完成后替换 fallback 内容
你可以通过浏览器的 Network 面板观察每个页面组件的加载时机和体积变化。
七、总结
懒加载是构建高性能 React 应用的关键一步,尤其适用于中大型项目。希望这篇文章能帮助你在实际项目中更好地应用这一技术!