阅读视图

发现新文章,点击刷新页面。

前端路由不再难:React Router 从入门到工程化

掌握 React Router:从基础到工程化实践

前端路由:从页面跳转到体验升级

在 Web 开发的早期,前端只是“静态页面的搬运工”——所有跳转逻辑由后端掌控,每次点击链接都会触发一次完整的页面刷新。用户看到的是白屏、等待、再加载;开发者面对的是耦合、重复和低效。

随着前后端分离架构的普及,前端不再只是展示层,而是逐渐承担起完整的应用逻辑。路由,也由此从前端的“盲区”变成了核心能力之一

今天,借助像 React Router 这样的工具,我们可以在不刷新页面的前提下,实现 URL 与 UI 的精准同步,构建真正意义上的单页应用(SPA)——既保持了 URL 的可分享性与书签能力,又带来了接近原生应用的流畅体验。

而这一切,都始于一个简单的映射关系:
路径(URL) → 组件(View)

接下来,我们将深入 React Router DOM,从安装配置到高级用法,一步步揭开前端路由的面纱。

React Router DOM

不同框架的路由实现虽然不同,但基本原理是相似的。这里我们以 React 路由为例,带你了解路由的魅力。

传统的 Web 开发是多页应用,基于 HTTP 请求实现。每当请求新的 URL 时,页面都会完全刷新,导致瞬间白屏,体验较差。

单页应用 (SPA)

得益于路由的出现,我们能够实现单页应用(SPA)。当用户点击链接跳转时,只触发对应的事件并更新局部视图,而不是重新渲染整个页面,从而提供流畅的用户体验。

安装路由

npm install react-router-dom

路由表示的就是路径和组件的映射关系

路由的使用

1. 引入路由
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
2. 路由的模式
  • HashRouter (#/)
    • URL 带有 # 号。
    • 兼容性极好,通常在路径之后会自动加上 #/
  • BrowserRouter (/)
    • URL 和后端路由一致,基于 HTML5 History API。
    • 兼容性较好(IE11 之前不支持),现代浏览器几乎都支持。
    • 推荐:现代开发中更推荐使用 BrowserRouter
    • 注意BrowserRouter as Router 是为了提高可读性,后续统一使用 Router 命名。
3. 核心组件
  • Routes:路由容器,负责管理一组路由,通常包裹在 Router 组件中。
  • Route:定义单个路由规则,当路径匹配时渲染对应的组件。

路由的配置

在现代的开发目录中,我们通常将路由的配置都放在 router 目录下。

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './Home';
import About from './About';

<Router>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
</Router>

配置说明

  • / 对应 Home 组件
  • /about 对应 About 组件

当用户访问 / 时,会渲染 Home 组件;当访问 /about 时,会渲染 About 组件。

路由导航

在 SPA 中,我们不能使用 <a> 标签进行导航,因为它会导致页面的刷新。我们需要使用 React Router 提供的 Link 组件。

当用户点击 Link 组件时,会触发路由的导航,而不会导致页面刷新。我们需要在 Link 组件中指定 to 属性,来指定跳转的路径。

import { Link } from 'react-router-dom';

<Link to="/">Home</Link>
<Link to="/about">About</Link>

点击对应的链接就会跳转到对应的页面。


路由的种类

1. 普通路由

最基本的路由,当路径匹配时,会渲染对应的组件。 例如:about 对应 About 组件。

 <Route path="/about" element={<About />} />
2. 动态路由

路径中包含动态参数。

<Route path="/user/:id" element={<UserProfile />} />

动态路由当路径匹配时,会渲染对应的组件,并且会将路径中的参数传递给组件。 例如:/user/123 对应 UserProfile 组件,并且会将 123 传递给 UserProfile 组件。

UserProfile 组件中,我们可以通过 useParams hook 来获取路由参数:

import { useParams } from 'react-router-dom';
const { id } = useParams();
console.log(id); // 123
3. 嵌套路由
<Route path="/products" element={<Product/>}>
    <Route path=":productId" element={<ProductDetail />}/>
    <Route path="new" element={<NewProduct />}/>
</Route>

嵌套路由是局部更新的一种典型。当用户访问到产品页面时,点击产品列表中的不同按钮(例如产品详情和新产品),会渲染对应的子组件,而不会导致整个页面的刷新。 这样的路径配置是基于 /products 继续匹配的。

Product 组件中,我们可以通过 Outlet 组件来渲染嵌套路由的内容。 例如:/products/123 对应 ProductDetail 组件,并且会将 123 传递给 ProductDetail 组件。

import{
    Outlet
} from 'react-router-dom'
export default function Product() {
  return (
    <>
      <h1>产品列表</h1>
      <Outlet />
    </>
  )
}
4. 重定向路由
import {
  Routes, 
  Route,
  Navigate
} from 'react-router-dom'

<Route path="/old-path" element={<Navigate to="/new-path" />} />

当一个网站可能已经废弃或者不再更新资源,但用户依然访问旧路径时,我们通常就需要使用重定向路由帮助用户进行导航。 引入 Navigate,当用户访问 /old-path 时,会自动跳转到 /new-path。默认是使用 replace 模式,不会保留旧的历史记录。

如果我们想要保留旧的历史记录,可以设置 replace 属性为 false

<Route path="/old-path" element={<Navigate to="/new-path" replace={false} />} />
5. 鉴权路由 (路由守卫)
import ProtectRoute from './ProtectRoute';
<Route path='/pay' element={
   <ProtectRoute>
    <Pay />
   </ProtectRoute>
}/>
//protectRoute.jsx
import { Navigate } from 'react-router-dom'
export default function ProductRoute({children}) {
    const isLoggedIn = localStorage.getItem('isLogin')==='true'
    if(!isLoggedIn){
        return <Navigate to="/login" />
    }
  return (
    <>
      {children}
    </>
  )
}

鉴权路由也叫路由守卫,只有当满足条件时才能够放行。就像很多软件,你不登录就无法使用很多功能;当你想要付款时,如果没有登录,它就会自动弹出登录页。 使用一个组件来包裹需要鉴权的路由,当用户访问到这个路由时,会先判断是否登录。如果没有登录,就会跳转到登录页,否则就会渲染对应的组件。

6. 404 路由
<Route path="*" element={<NotFound />} />

* 代表除配置外的所有路由。 当用户访问到一个不存在的路由时,会渲染 NotFound 组件。


性能优化

路由的懒加载

在现代前端框架(如 React、Vue 等)中,路由的懒加载 (Lazy Loading) 是一种优化应用性能的重要手段。它允许你按需加载组件,而不是在应用初始加载时一次性加载所有路由组件,从而减小首屏 bundle 体积、加快页面加载速度。

例如当你打开应用首页时,你会看见很多功能按钮,但是如果要在首次进入时把这些按钮对应的所有组件一次性全部加载,这无疑是很糟糕的。这样不仅仅卡顿,还会增加首屏加载时间,甚至有些组件你都不使用,它却被加载了,这是非常浪费资源的。

而懒加载就很好地处理了这个问题。

// 传统的引入
import Home from './Home';
import About from './About';

// 懒加载引入
import { lazy } from 'react';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

当然了,所有懒加载的组件都需要在 Suspense 组件中包裹起来,否则会报错。

import { Suspense } from 'react';
<Suspense fallback={<div>Loading...</div>}>
   <Route path="/" element={<Home />} />
   <Route path="/about" element={<About />} />
</Suspense>

HomeAbout 组件被加载时,会显示 Loading...,当加载完成后,会显示对应的组件。 当我们访问 / 时,会显示 Home 组件,而 About 则会延迟加载;只有当用户访问到 /about 时,才会加载 About 组件。


路由职责分离

为了保持代码的整洁和可维护性,我们通常会将路由的配置导航逻辑从主应用组件中剥离出来。 这种分离让 App.jsx 更加简洁,专注于布局和全局组件的组合。

1. 独立的路由配置 (src/router/index.jsx)

所有的路由规则、懒加载 (lazy)、路由守卫 (ProtectRoute) 等逻辑都集中管理。

// src/router/index.jsx
import { Routes, Route } from 'react-router-dom';
import { lazy, Suspense } from 'react';

// ... 懒加载组件定义 ...

export default function RouterConfig(){
    return(
        <Suspense fallback={<LoadingFallback />}>
          <Routes>
            <Route path="/" element={<Home />} />
            {/* ...其他路由配置 */}
          </Routes>
        </Suspense>
    )
}
2. 独立的导航组件 (src/components/Navigation.jsx)

导航菜单、链接高亮等 UI 逻辑封装在独立的组件中。

// src/components/Navigation.jsx
import { Link } from 'react-router-dom';
export default function Navigation() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      {/* ... */}
    </nav>
  )
}
3. 简洁的主入口 (src/App.jsx)

主组件只负责引入和组装,不再包含复杂的路由逻辑。

// src/App.jsx
import Navigation from './components/Navigation'
import RouterConfig from './router'

export default function App() {
  return (
    <Router>
      <Navigation />   {/* 导航区域 */}
      <RouterConfig /> {/* 路由视图区域 */}
    </Router>
  )
}

样式优化

导航区高亮 (像掘金一样)

当我们点击导航区的不同链接时,我们通常会希望对应的导航项高亮显示,以告知用户当前所在的位置。 React Router 提供的 useResolvedPathuseMatch 可以帮助我们实现这个功能。

  • useResolvedPath: 将目标路径(to 属性)解析为绝对路径对象。
  • useMatch: 接收一个路径模式,判断当前 URL 是否与该模式匹配。
import { useResolvedPath, useMatch, Link } from 'react-router-dom'

export default function Navigation(){
  const isActive = (to) => {
     // 1. 将 to 解析为 location 对象,处理相对路径等情况
     const resolvedPath = useResolvedPath(to);
     
     // 2. 检查当前路径是否匹配 resolvedPath.pathname
     const match = useMatch({
      path: resolvedPath.pathname,
      end: true // 精准匹配,避免 / 匹配所有路径
     })

     return match ? 'active' : ''
  }
  
  return (
    <nav>
      <ul>
        <li><Link to="/" className={isActive('/')}>Home</Link></li>
        <li><Link to="/about" className={isActive('/about')}>About</Link></li>
        {/* ... */}
      </ul>
    </nav>
  )
}

结语

前端路由的本质,是路径与组件的映射;而 React Router,则让这种映射变得清晰、灵活又强大。

从基础跳转到动态参数,从嵌套路由到权限控制,再到懒加载与工程化拆分——我们不仅是在配置 URL,更是在构建一个可维护、高性能、用户体验流畅的现代 Web 应用。

掌握 React Router,不只是学会几个 API,而是理解 SPA 背后的导航逻辑与架构思维。希望这篇指南能成为你开发路上的可靠参考,助你写出更优雅的路由代码。

❌