🛬 前端路由的"高级玩法":懒加载、404、鉴权路由,一个都不能少(下篇)

写在前面:上篇讲了 React Router 的入门三件套(路由选型、Link、Routes/Route 配置),这篇是进阶篇------也是"更实用"的部分。老师说,真实项目里的路由不只是"配置一下路径"那么简单:页面太多要懒加载 (不然首页加载卡死)、用户没登录要拦截 (鉴权路由)、老链接要重定向 、访问不存在的路径要显示 404。这篇内容满满,都是干货,建议收藏。


一、路由懒加载:性能优化的"必修课"

1.1 问题:全部页面一次性加载

如果一个项目有几十个页面,全部用静态 import 加载:

jsx 复制代码
import Home from './pages/Home';
import About from './pages/About';
import UserProfile from './pages/UserProfile';
// ...几十个页面

问题:用户访问首页时,几十个页面全部下载执行------首页加载速度慢得离谱。

老师说:

"首页/页面加载速度------非当前路由页面不加载。性能优化。"

1.2 解决方案:lazy + Suspense

jsx 复制代码
import { lazy, Suspense } from 'react';

// 路由懒加载:点击时才加载对应页面
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const UserProfile = lazy(() => import('./pages/UserProfile'));
const Login = lazy(() => import('./pages/Login'));
const Pay = lazy(() => import('./pages/Pay'));

lazy(() => import(...)) 是动态导入------页面组件只有在需要渲染时才会被加载。

还需要用 Suspense 包裹,加载过程中显示 fallback:

jsx 复制代码
<Router>
    <Suspense fallback={<div>Loading...</div>}>
        <Navigation />
        <div id="container">
            <Routes>
                {/* 路由配置 */}
            </Routes>
        </div>
    </Suspense>
</Router>

Suspensefallback 是加载过程中的"占位内容"------页面在下载时,先显示"Loading..."。

对比 静态 import 懒加载(lazy)
加载时机 项目启动时全部加载 访问到该路由时才加载
首页速度 慢(所有页面都下载) 快(只下载首页)
代码分割 自动按路由拆分

二、重定向:Navigate 组件

2.1 场景

老师说:

"有个活动 /game 100 万,/result 活动结束了;/home 首页,重定向到 /。"

旧链接失效了、活动结束了、地址换新了------都需要"重定向",把用户送到新地址。

2.2 实现

jsx 复制代码
{/* 老路径跳转到新路径 */}
<Route path="old-path" element={
    <Navigate replace to="/new-path" />
} />

<Navigate replace to="/new-path" /> 是一个"自动跳转"的组件:

  • to:跳转目标。
  • replace:替换当前历史记录(用户按"返回"不会回到旧页面)。

这就像你在前台贴了一张公告:"我们搬家了,新地址在这边。"


三、404 页面:最后的"兜底"

3.1 问题

用户输入了一个不存在的路径(比如 /abc),会发生什么?

答案是:什么都不显示------空白页。 这是很差的体验。

3.2 解决方案:贪婪匹配

jsx 复制代码
{/* "*" 贪婪匹配所有,最后 404 兜底 */}
<Route path="*" element={<NotFound />} />

老师说:

"* 贪婪匹配所有,最后 404 兜底。"

path="*" 会匹配所有没有被其他路由匹配到的路径。

关键:这个 Route 必须放在最后! 因为 Routes 是按顺序匹配的,前面的路径都试过了都不行,才会落到 * 上。

bash 复制代码
/           → Home
/about      → About
...(前面的路由优先匹配)
/abc        → 前面都不匹配 → * 兜底 → NotFound

四、鉴权路由:ProtectRoute 守卫

4.1 什么是鉴权路由?

场景:支付页面必须登录才能访问。 未登录用户直接访问 /pay,应该被"拦下来"送到登录页。

老师说:

"HTTP 无状态的。有状态?① 请求头 token Authorization ② Cookie 小饼干 ③ localStorage 存储 login 状态。"

这里我们用 localStorage 存储登录状态。

4.2 ProtectRoute 实现

ProtectRoute.jsx

jsx 复制代码
import { Navigate } from 'react-router-dom';

const ProtectRoute = ({ children }) => {
    // 拦截请求,鉴权
    // HTML5 本地存储,域名的沙盒
    const isLogin = localStorage.getItem('isLogin') === 'true';

    if (!isLogin) {
        // 如果未登录,跳转到登录页
        // 路由设置 state 状态对象,记录从哪里来
        return <Navigate to="/login" replace state={{ from: location.pathname }} />
    }

    return (
        <>
            {children}
        </>
    );
};

export default ProtectRoute;

ProtectRoute 是一个"门禁保安":

bash 复制代码
用户访问 /pay
  ↓
ProtectRoute 检查 localStorage.getItem('isLogin')
  ↓
未登录 → <Navigate to="/login"> 送回登录页
已登录 → 渲染 {children}(放行)

关键细节:state={{ from: location.pathname }} ------记录用户"从哪里来"。登录成功后,可以跳回原页面(比如用户想访问 /pay,登录后直接送回 /pay)。

4.3 在路由中如何使用

jsx 复制代码
<Route path="/pay" element={
    // 门禁保安
    // Pay 是要进的房间
    // children 用来定制化组件
    <ProtectRoute>
        {/* children */}
        <Pay />
    </ProtectRoute>
} />

<Pay /> 被作为 children 传给了 <ProtectRoute> 登录了,children 被渲染;没登录,直接送去登录页。


五、children:组件的"定制化插槽"

5.1 children 是什么?

老师说:

"组件内部的子组件------props.children 拿到组件声明的内部所有的子节点。"

jsx 复制代码
<ProtectRoute>
    <Pay />   {/* 这就是 children */}
</ProtectRoute>

在 ProtectRoute 里,props.children 就是 <Pay />

5.2 children 的经典场景:弹窗(Modal)

老师说:

"Model 弹窗组件,mask 蒙层,窗体头部、尾部、主体部分 children 传入。定制性。"

jsx 复制代码
const Modal = ({ children }) => {
    return (
        <div className="mask">       {/* 蒙层 */}
            <div className="window">
                <header>标题</header>
                <div className="body">
                    {children}        {/* 主体内容由调用者定制 */}
                </div>
                <footer>按钮区</footer>
            </div>
        </div>
    );
};

// 使用:想放什么内容就放什么
<Modal>
    <p>确认要删除吗?</p>
</Modal>

<Modal>
    <form>...</form>
</Modal>

同一个 Modal 组件,通过 children 可以装任何内容------这就是"定制性"。


六、完整的路由配置(总结)

App.jsx 的完整配置:

jsx 复制代码
<Routes>
    {/* 基本配置 */}
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    {/* 动态路由 */}
    <Route path="/user/:id" element={<UserProfile />} />
    {/* 嵌套路由 */}
    <Route path="/products" element={<Products />}>
        <Route path=":productId" element={<ProductDetail />} />
        <Route path="new" element={<NewProduct />} />
    </Route>
    {/* 重定向 */}
    <Route path="old-path" element={<Navigate replace to="/new-path" />} />
    <Route path="/login" element={<Login />} />
    {/* 鉴权路由 */}
    <Route path="/pay" element={
        <ProtectRoute>
            <Pay />
        </ProtectRoute>
    } />
    {/* 404 兜底 */}
    <Route path="*" element={<NotFound />} />
</Routes>

一个完整项目的路由,通常包含:基本配置 + 动态路由 + 嵌套路由 + 重定向 + 鉴权 + 404。六种玩法,缺一不可。


七、总结(下篇)

概念 说明
懒加载 lazy(() => import()) + Suspense,按需加载页面
fallback 加载过程中的占位内容
Navigate 重定向组件,to + replace
404 兜底 path="*" 贪婪匹配所有未匹配路径
鉴权路由 ProtectRoute 检查登录状态,未登录送去登录页
children 组件内部子节点,实现"定制化插槽"
state 传参 state={{ from: location.pathname }} 记录来源

路由懒加载优化性能,重定向和 404 兜底体验,鉴权路由保安全,children 定制化组件------这套组合拳,就是真实项目路由的完整形态。


写在最后

上下两篇加起来,我把 React Router 的完整知识体系梳理完了。从路由选型、Link 导航、基本配置,到动态路由、嵌套路由、懒加载、重定向、404、鉴权......虽然内容很多,但核心思想就一条:URL 和资源一一对应,页面无刷新切换。

下次面试官问你:"React Router 怎么做鉴权路由?"

你可以淡定地说:

"React Router 做鉴权路由的核心是封装一个 ProtectRoute 守卫组件。它接收 children,在渲染前检查登录状态------比如 localStorage.getItem('isLogin') === 'true'。如果未登录,返回 <Navigate to="/login" replace state={{ from: location.pathname }} /> 跳转到登录页,并通过 state 记录用户来源路径,登录成功后可以跳回原页面;如果已登录,直接渲染 children。使用时把需要保护的路由包起来:<Route path="/pay" element={<ProtectRoute><Pay /></ProtectRoute>} />。另外配合 lazy 懒加载优化性能、path="*" 做 404 兜底、Navigate 做重定向,就是完整的路由方案。"

然后看着面试官满意的表情,心里默念:这波,又稳了。


本文所有代码示例均来自课堂学习资料,真实可运行。

相关推荐
sunly_1 小时前
TypeScript:3、类型声明与类型推断
javascript·ubuntu·typescript
用户921080262861 小时前
1. Cesium 在 Vue 项目中的简单初始化配置
前端
YIAN1 小时前
从 Hash 底层原理到 React Router v6 实战:我学会了什么?
前端·react.js·vue-router
lv__pf2 小时前
Spring配置类解析 【TL spring 11】
java·前端·spring
AI分享猿2 小时前
UI设计Prompt系列(十三):响应式布局需求怎么写——让设计Prompt更接近前端实现
前端
ClouGence2 小时前
Selenium 写不动了?这个工具录一次就能跑 Web 自动化
前端·selenium·测试
hunterandroid2 小时前
[鸿蒙从零到一] HarmonyOS 地图、定位与传感器能力实战:从位置获取到运动感知
前端
大锅盖12 小时前
Web 工单要调用相机,第一步不是打开取景框,而是建立能力门禁
前端·数码相机·harmonyos
fthux2 小时前
不必下载整个仓库:GitZip Pro 让 GitHub 文件与文件夹批量下载更简单
前端·chrome·ai·edge·开源·github·firefox