React 路由懒加载详解:优化你的前端性能

572 阅读6分钟

在现代的前端开发中,路由懒加载(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:

  • BrowserRouterreact-router-dom提供的一个组件,它使用HTML5history 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>
  )
}

这里我们引入了两个页面组件:HomeAbout


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.导航栏封装成componentsNavigation中引入路由

// 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 />在这里有什么问题呢?通过对HomeAbout设置打印结果可以看到,每次当首页渲染的时候,About页面也会随之渲染。当HomeAbout的页面内容巨大的时候,页面加载的速度就会变的十分缓慢,为此我们需要新的工具来解决该问题! image.png


⏳ 三、为什么需要路由懒加载?

默认情况下,React 在初始化时就会同步加载所有页面组件。例如上面的代码中,即使用户只访问了 /Home,也会同时加载 HomeAbout 页面的内容。

这会导致:

  • 首屏加载变慢
  • 资源浪费(未访问的页面也加载了)

特别是在大型项目中,这种问题尤为明显。因此我们需要引入 懒加载机制


四、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...

NotFound.gif

当我们访问的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 加载并执行太多的非必要组件,影响首页的加载速度,特别是页面多的时候。因此为解决路由的懒加载,让我们来看看下面实现懒加载的流程。

  1. 用户访问首页 → 只加载 Home 组件
  2. 点击“关于”链接 → 触发 /about 路由匹配
  3. React.lazy() 动态导入 About 组件
  4. 加载期间显示 fallback 内容(如 loading)
  5. 组件加载完成后替换 fallback 内容

你可以通过浏览器的 Network 面板观察每个页面组件的加载时机和体积变化。

懒加载network.gif


七、总结

懒加载是构建高性能 React 应用的关键一步,尤其适用于中大型项目。希望这篇文章能帮助你在实际项目中更好地应用这一技术!