前言:
上一文我们讲解了原生JS如何用 hashChange 和 pushState 实现两种路由方案,通过拦截URL变化事件、阻止浏览器默认刷新行为并动态更新页面内容,解决了传统多页应用(MPA)中URL变化必触发整页刷新的核心问题,消除了页面跳转时的白屏现象和资源重复加载,同时保留了浏览器前进/后退导航能力,最终实现了无刷新的单页应用(SPA) 体验,显著提升了Web应用的响应速度和用户体验连续性。
然而,当应用规模扩大到包含数十个页面、复杂的嵌套路由关系和精细的权限控制时,原生路由方案逐渐暴露出局限性:手动维护路由与组件的映射关系导致代码冗余、路由守卫逻辑分散在各个事件处理函数中难以管理、缺乏统一的路由状态管理机制等问题开始显现。当页面数量增多时,如何优化加载性能(如路由懒加载)也成为亟待解决的问题。
这时候React Router就站出来了。它把路由逻辑包装成组件,用React的方式解决路由问题,让我们能专注写业务代码。今天就聊聊React Router到底是怎么让Web体验从「卡成PPT」变成「丝滑如德芙」的。
React Router是个啥?先看最基本的用法
React Router本质上就是个「页面导航管理员」。你告诉它哪个URL对应哪个页面组件,它就会在URL变化时,把对应的组件显示出来,其他内容不动。
先看个最简单的例子,这是我项目里 App.jsx 的基础结构:
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
function App() {
return (
<Router>
{/* 导航链接 */}
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于我们</Link>
</nav>
{/* 路由匹配规则 */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
)
}
这里面有几个核心角色:
- Router :整个路由系统的「容器」,一般用
BrowserRouter(基于HTML5的history API) - Link :导航链接,类似
<a>标签但不会刷新页面 - Routes :路由规则的「包裹器」,会自动选择匹配的路由显示
- Route :单条路由规则, path 是URL路径, element 是要显示的组件 用这几行代码,就能实现点击导航时页面内容局部更新,URL也跟着变,还不会刷新页面——这就是 SPA 的基础体验。
解决实际问题:从登录保护到404页面
光有基础路由还不够,实际项目里总有各种特殊情况。我记了几个常见场景,咱们一个个说。
1. 登录了才能看的页面——路由守卫
有些页面(比如支付页 pay)必须登录才能访问,这时候就需要「路由守卫」。我项目里写了个 <ProtectRoute> 组件:
// 鉴权组件
import { Navigate,useLocation } from 'react-router-dom'
const ProtectRoute = (props)=>{
console.log(props);
// 并非子组件
// children 属性 提升定制性
const {children} = props
const {pathname} = useLocation()
const isLogin = localStorage.getItem('isLogin') === 'true'
console.log(pathname);
if(!isLogin){
return <Navigate to='/login' state={{from:pathname}} />
}
return (children)
}
export default ProtectRoute
原理很简单:判断用户是否登录,没登录就用 <Navigate> (React Router v6+的重定向组件)跳转到登录页。这样就不用在每个需要权限的页面里重复写判断了。
并且 state={{from:pathname}} 这段代码实现了「登录后返回原页面」的完整流程,核心通过 React Router 的状态传递机制实现:
- pathname 是当前尝试访问的页面路径(例如用户直接访问 /pay 时, pathname 就是 /pay )
- 通过 state={{from: pathname}} 将原页面路径存入路由状态中,类似「给登录页留个纸条:用户本来想去/pay」
登录成功之后,我们成功返回到原先的 pay支付页面。
2. 输错URL怎么办?404页面
用户可能会输错URL,这时候总不能显示空白吧?加个「全匹配路由」就行,放在所有路由的最后:
// 404页面组件
const NotFound = () => {
return <div>哎呀,页面走丢了~</div>
}
// 路由配置
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* 前面都没匹配到,就显示404 */}
<Route path='*' element={<NotFound />} />
</Routes>
path='*' 表示匹配所有URL,放在最后就能兜底处理404情况。
性能优化:从「卡一下」到「秒开」
用了路由之后页面切换是不刷新了,但如果页面太多,首次加载还是会很慢。我在项目README里特意记了一句:「import太多非必要组件,影响首页的加载速度,特别是页面多的时候」。
这时候「路由懒加载」就派上用场了——只加载当前页面需要的组件,其他页面等用户点击了再加载。实现起来也简单,React自带的 lazy 和 Suspense 就能搞定:
import { lazy, Suspense } from 'react'
// 不再直接import,而是用lazy动态加载
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
function App() {
return (
<Router>
{/* 加载过程中显示loading */}
<Suspense fallback={<div>加载中...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
)
}
这样打包的时候,每个页面组件会被拆成单独的JS文件,首页加载时只需要加载核心代码,速度能快不少。我测试过,30个页面的项目用了懒加载,首页加载时间从3秒降到了1秒以内。
写在后面:路由不只是「跳转」
刚开始我以为路由就是「控制页面跳转」,用了React Router才发现,它解决的是「如何让Web应用像原生App一样流畅」。从阻止白屏到优化加载速度,从权限控制到错误处理,这些细节加起来,才让用户觉得「这个网站用着真舒服」。
总结
回顾我们对前端路由的探索,从原生JS的 hashChange 和 pushState 方案,到 React Router的组件化实现,本质上都是在解决同一个核心问题——如何让Web应用摆脱频繁整页刷新的束缚,实现如原生应用般流畅的体验。
React Router作为React生态的重要组成部分,通过将路由逻辑组件化,不仅简化了路由配置(如 <Routes> 和 <Route> 的声明式用法),更提供了应对复杂场景的完整方案:用 <ProtectRoute> 处理权限控制,用 path='*' 捕获404错误,用 React.lazy 和 Suspense 实现路由懒加载解决性能问题。这些特性共同构成了现代单页应用的路由基石。对于开发者而言,掌握这些路由技术不仅是完成功能的需要,更是构建优质用户体验的基础。
最后留个小问题:如果用户刷新页面,懒加载的组件会重新加载吗?