1. 现代 React 路由演进史
前端路由的发展历程可以追溯到早期的 Web 开发时代。在传统 Web 应用中,每次页面跳转都需要向服务器发送请求,导致整个页面重新加载,用户会经历短暂的白屏等待。这种体验在今天的标准看来已经难以接受。
随着 AJAX 技术的普及,单页应用(SPA)开始流行。React、Vue 等现代前端框架的出现,使得前端路由成为可能。前端路由的核心思想是:在浏览器端拦截导航请求,通过 JavaScript 动态更新页面内容,避免整页刷新。
提示:现代前端路由的实现依赖于浏览器提供的 History API 或 hashchange 事件,这是理解路由机制的基础。
2. 路由实现方式对比
2.1 HashRouter 深度解析
HashRouter 利用 URL 中的 hash 片段(#)来实现路由功能。它的工作原理是监听 window.onhashchange 事件,当 URL 的 hash 部分发生变化时,触发路由更新。
技术实现细节:
- URL 格式:
http://example.com/#/path - 兼容性:支持 IE8+ 等几乎所有浏览器
- 服务器要求:无需特殊配置,适合静态托管
- 实现原理:
javascript复制// 简化的 HashRouter 实现 window.addEventListener('hashchange', () => { const path = window.location.hash.slice(1) || '/' // 根据 path 渲染对应组件 })
实际项目中的选择建议:
- 当项目需要部署在 GitHub Pages 等静态托管平台时
- 需要支持老旧浏览器(如 IE9-11)时
- 后端无法配置 URL 重定向规则时
2.2 BrowserRouter 技术内幕
BrowserRouter 基于 HTML5 History API 实现,提供了更干净的 URL 结构。它使用 pushState 和 replaceState 方法来操作浏览器历史记录。
核心技术要点:
- URL 格式:
http://example.com/path - 依赖:HTML5 History API
- 服务器要求:需要配置 URL 重定向(通常将所有请求指向 index.html)
- 实现原理:
javascript复制// 简化的 BrowserRouter 实现 window.addEventListener('popstate', () => { const path = window.location.pathname // 根据 path 渲染对应组件 }) // 导航时调用 history.pushState({}, '', '/new-path')
生产环境部署要点:
- Nginx 配置示例:
nginx复制location / { try_files $uri $uri/ /index.html; } - Apache 配置示例:
apache复制RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]
3. 路由懒加载实战指南
3.1 React.lazy 与 Suspense 原理
React.lazy 使用动态 import() 语法来实现代码分割。当 Webpack 遇到这种语法时,会自动将引用的模块拆分成单独的 chunk。
技术实现细节:
javascript复制const LazyComponent = React.lazy(() => import('./SomeComponent'))
// 使用方式
function MyComponent() {
return (
<Suspense fallback={<LoadingSpinner />}>
<LazyComponent />
</Suspense>
)
}
性能优化数据:
- 未使用懒加载:假设应用总大小 500KB,首屏需要加载全部
- 使用懒加载后:首屏仅加载 150KB 核心代码,其余按需加载
- 典型性能提升:首屏加载时间减少 40-60%
3.2 懒加载最佳实践
-
路由级拆分:
javascript复制const Home = lazy(() => import('./pages/Home')) const About = lazy(() => import('./pages/About')) -
避免过度拆分:
javascript复制// 不推荐 - 过度拆分导致过多小请求 const Button = lazy(() => import('./components/Button')) const Input = lazy(() => import('./components/Input')) -
预加载策略:
javascript复制// 鼠标悬停时预加载 <Link to="/about" onMouseEnter={() => import('./pages/About')} > 关于我们 </Link>
注意事项:懒加载组件必须使用 default export,命名导出需要额外处理:
javascript复制const About = lazy(() => import('./pages/About').then(module => ({ default: module.About })))
4. 高级路由模式详解
4.1 动态路由实战
动态路由允许在 URL 中传递参数,非常适合详情页等场景。
完整实现示例:
javascript复制// 路由配置
<Route path="/products/:id" element={<ProductDetail />} />
// 组件内获取参数
import { useParams } from 'react-router-dom'
function ProductDetail() {
const { id } = useParams()
// 使用 id 获取数据...
}
进阶技巧:
- 类型安全的参数:使用 TypeScript 确保参数类型
typescript复制type Params = { id: string } const { id } = useParams<Params>() - 参数验证:在加载数据前验证参数有效性
javascript复制if (!isValidId(id)) { return <Navigate to="/not-found" /> }
4.2 嵌套路由架构设计
嵌套路由允许共享布局,减少代码重复。React Router v6 使用 Outlet 组件作为子路由的渲染位置。
企业级项目结构示例:
code复制src/
layouts/
MainLayout.tsx
AuthLayout.tsx
pages/
Home/
index.tsx
components/
Products/
index.tsx
[id].tsx
New.tsx
路由配置示例:
javascript复制<Route element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />}>
<Route path=":id" element={<ProductDetail />} />
<Route path="new" element={<NewProduct />} />
</Route>
</Route>
布局组件实现:
javascript复制function MainLayout() {
return (
<div className="app">
<Header />
<Sidebar />
<main>
<Outlet /> {/* 子路由在此渲染 */}
</main>
<Footer />
</div>
)
}
4.3 鉴权路由安全实践
前端路由鉴权是保护敏感页面的第一道防线(注意:后端验证仍是必须的)。
增强版鉴权组件:
javascript复制function ProtectedRoute({ roles, children }) {
const auth = useAuth()
const location = useLocation()
if (!auth.user) {
return <Navigate to="/login" state={{ from: location }} replace />
}
if (roles && !roles.includes(auth.user.role)) {
return <Navigate to="/unauthorized" replace />
}
return children
}
使用示例:
javascript复制<Route
path="/admin"
element={
<ProtectedRoute roles={['admin', 'superadmin']}>
<AdminDashboard />
</ProtectedRoute>
}
/>
安全注意事项:
- 永远不要仅依赖前端鉴权
- 敏感操作必须经过后端验证
- JWT 令牌应设置合理有效期
- 实现适当的 CSRF 防护
4.4 重定向高级用法
React Router 提供了多种重定向方式,适用于不同场景。
-
永久重定向(301):
javascript复制<Route path="/old" element={<Navigate to="/new" replace />} /> -
临时重定向(登录后返回):
javascript复制function Login() { const location = useLocation() const navigate = useNavigate() const handleLogin = () => { login().then(() => { navigate(location.state?.from || '/', { replace: true }) }) } return <button onClick={handleLogin}>登录</button> } -
条件重定向:
javascript复制function Home() { const { user } = useAuth() if (user?.isNewUser) { return <Navigate to="/welcome" replace /> } return <HomeContent /> }
5. 性能优化与调试
5.1 路由性能分析工具
-
Chrome DevTools 的 Coverage 面板:
- 查看未使用的代码比例
- 分析懒加载效果
-
React DevTools:
- 检查组件加载状态
- 分析 Suspense 边界
-
Webpack Bundle Analyzer:
bash复制
npm install --save-dev webpack-bundle-analyzer配置:
javascript复制const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin module.exports = { plugins: [new BundleAnalyzerPlugin()] }
5.2 常见问题排查
-
白屏问题:
- 检查 Suspense fallback 是否正确定义
- 验证动态导入路径是否正确
- 确保服务器配置正确(BrowserRouter)
-
路由不匹配:
- 检查路径大小写(React Router 默认区分大小写)
- 使用
<Routes>包裹所有<Route> - 确保没有重复的路由定义
-
导航循环:
- 避免在渲染过程中无条件重定向
- 检查鉴权逻辑中的条件判断
-
生产环境路由失效:
- 验证服务器配置是否正确
- 检查 base URL 设置
- 确保静态资源路径正确
6. 测试策略
6.1 单元测试路由组件
使用 Jest 和 Testing Library 测试路由逻辑:
javascript复制import { render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom'
test('renders home page', () => {
render(
<BrowserRouter>
<App />
</BrowserRouter>
)
expect(screen.getByText('Welcome')).toBeInTheDocument()
})
测试动态路由:
javascript复制test('displays product details', async () => {
render(
<MemoryRouter initialEntries={['/products/123']}>
<App />
</MemoryRouter>
)
await screen.findByText('Product 123')
})
6.2 E2E 测试路由导航
使用 Cypress 进行端到端测试:
javascript复制describe('Navigation', () => {
it('should navigate to about page', () => {
cy.visit('/')
cy.get('a[href="/about"]').click()
cy.url().should('include', '/about')
cy.contains('About Us')
})
})
测试鉴权流程:
javascript复制it('should redirect unauthorized users', () => {
cy.visit('/dashboard')
cy.url().should('include', '/login')
cy.contains('Please log in')
})
7. 项目结构建议
7.1 中小型项目结构
code复制src/
components/
pages/
Home.tsx
About.tsx
products/
index.tsx
[id].tsx
routes/
index.tsx
PrivateRoute.tsx
App.tsx
7.2 大型企业级项目结构
code复制src/
features/
auth/
components/
pages/
routes.ts
products/
components/
pages/
api/
routes.ts
app/
layouts/
routes.tsx
store/
shared/
components/
hooks/
utils/
路由聚合模式:
javascript复制// src/app/routes.tsx
import authRoutes from '../features/auth/routes'
import productRoutes from '../features/products/routes'
const routes = [
...authRoutes,
...productRoutes,
// 其他路由...
]
export default function AppRoutes() {
return (
<Routes>
{routes.map((route) => (
<Route key={route.path} {...route} />
))}
</Routes>
)
}
8. 未来演进与替代方案
8.1 React Router 与其他方案对比
-
Next.js 文件系统路由:
- 基于文件结构自动生成路由
- 支持服务端渲染和静态生成
- 适合内容型网站和 SEO 敏感应用
-
Remix 路由:
- 嵌套路由数据加载
- 内置数据突变和表单处理
- 服务端渲染优先
-
TanStack Router:
- 完全类型安全
- 精细的性能优化
- 复杂状态管理集成
8.2 渐进式迁移策略
从 React Router 5 升级到 6 的步骤:
-
安装新版:
bash复制
npm install react-router-dom@6 -
主要变更点:
<Switch>改为<Routes>- 路由定义语法变化
- 嵌套路由使用新方案
-
兼容层方案:
javascript复制import { CompatRouter } from 'react-router-dom-v5-compat' function App() { return ( <CompatRouter> {/* 原有 v5 代码 */} </CompatRouter> ) }
9. 实战案例:电商网站路由设计
9.1 完整路由配置
javascript复制function AppRoutes() {
return (
<Routes>
<Route element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/cart" element={<Cart />} />
<Route path="/checkout" element={
<ProtectedRoute>
<Checkout />
</ProtectedRoute>
} />
<Route path="/account" element={
<ProtectedRoute>
<Account />
</ProtectedRoute>
} />
</Route>
<Route element={<AuthLayout />}>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
)
}
9.2 性能优化实践
-
关键路由预加载:
javascript复制// 在首页组件中预加载关键路由 useEffect(() => { import('./pages/ProductList') import('./pages/Cart') }, []) -
路由级代码分割:
javascript复制const ProductList = lazy(() => import( /* webpackPrefetch: true */ /* webpackChunkName: "product" */ './pages/ProductList' )) -
骨架屏优化:
javascript复制<Suspense fallback={<ProductSkeleton />}> <ProductDetail /> </Suspense>
10. 专家级技巧
10.1 自定义路由钩子
创建 useRouteBlocker 防止意外离开:
javascript复制function useRouteBlocker(shouldBlock) {
const navigate = useNavigate()
useEffect(() => {
if (!shouldBlock) return
const unblock = navigate.block((tx) => {
if (window.confirm('您有未保存的更改,确定要离开吗?')) {
unblock()
tx.retry()
}
})
return unblock
}, [shouldBlock, navigate])
}
10.2 路由动画技巧
使用 Framer Motion 实现路由过渡:
javascript复制import { AnimatePresence } from 'framer-motion'
function App() {
const location = useLocation()
return (
<AnimatePresence mode="wait">
<Routes location={location} key={location.pathname}>
<Route path="/" element={
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Home />
</motion.div>
} />
</Routes>
</AnimatePresence>
)
}
10.3 服务端渲染路由处理
Next.js 风格的数据加载:
javascript复制// 假设我们有一个支持 SSR 的包装器
async function loadRouteData(loader, context) {
if (typeof window === 'undefined') {
// 服务端执行
const data = await loader(context)
return { props: data }
} else {
// 客户端执行
const { data } = useQuery(loader.name, () => loader(context))
return { props: data }
}
}
// 路由组件使用
function ProductPage({ id }) {
const { props } = loadRouteData(fetchProductData, { id })
// ...
}
11. 路由与状态管理集成
11.1 URL 作为状态源
将搜索参数同步到状态:
javascript复制function useSearchParamsState() {
const [searchParams, setSearchParams] = useSearchParams()
const updateParam = (key, value) => {
const newParams = new URLSearchParams(searchParams)
if (value === undefined) {
newParams.delete(key)
} else {
newParams.set(key, value)
}
setSearchParams(newParams)
}
return [searchParams, updateParam]
}
11.2 Redux 与路由同步
使用 connected-react-router:
javascript复制import { connectRouter, routerMiddleware } from 'connected-react-router'
const history = createBrowserHistory()
const store = createStore(
combineReducers({
router: connectRouter(history),
// 其他 reducer...
}),
applyMiddleware(routerMiddleware(history))
)
// 组件中访问路由状态
const mapState = (state) => ({
pathname: state.router.location.pathname
})
12. 微前端路由方案
12.1 基于路由的微前端集成
主应用配置:
javascript复制<Routes>
<Route path="/*" element={<MainApp />} />
<Route path="/admin/*" element={<AdminApp />} />
<Route path="/dashboard/*" element={<DashboardApp />} />
</Routes>
子应用适配:
javascript复制// 子应用入口文件
function render({ routerBase }) {
ReactDOM.render(
<BrowserRouter basename={routerBase}>
<App />
</BrowserRouter>,
container
)
}
// 导出生命周期
export const mount = render
12.2 路由冲突解决策略
-
命名空间前缀:
javascript复制// 子应用路由 <Route path="/app1/products" element={<Products />} /> -
动态 basename:
javascript复制<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app1' : '/'}> <App /> </BrowserRouter> -
路由事件代理:
javascript复制// 主应用监听子应用路由变化 window.addEventListener('react-router', (e) => { // 处理子应用导航 })
13. 无障碍路由实践
13.1 键盘导航支持
确保路由链接可键盘操作:
javascript复制<Link
to="/about"
onKeyDown={(e) => {
if (e.key === 'Enter') {
navigate('/about')
}
}}
tabIndex={0}
>
关于我们
</Link>
13.2 焦点管理策略
路由变更后重置焦点:
javascript复制function AutoFocusRoute({ children }) {
const ref = useRef(null)
useEffect(() => {
ref.current?.focus()
}, [])
return <div ref={ref} tabIndex={-1}>{children}</div>
}
// 使用
<Route path="/" element={<AutoFocusRoute><Home /></AutoFocusRoute>} />
14. 国际化的路由处理
14.1 语言前缀路由
javascript复制<Route path="/:lang(en|zh)" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
</Route>
14.2 路由与 i18n 集成
javascript复制function useTranslatedRoutes() {
const { locale } = useI18n()
const navigate = useNavigate()
const tNavigate = (to, options) => {
const path = `/${locale}${to}`
navigate(path, options)
}
return tNavigate
}
15. 移动端路由优化
15.1 手势导航支持
javascript复制function SwipeRoute({ children }) {
const navigate = useNavigate()
const [startX, setStartX] = useState(null)
const handleTouchStart = (e) => {
setStartX(e.touches[0].clientX)
}
const handleTouchEnd = (e) => {
if (!startX) return
const diff = startX - e.changedTouches[0].clientX
if (Math.abs(diff) > 100) {
navigate(diff > 0 ? '/next' : '/prev')
}
}
return (
<div
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
{children}
</div>
)
}
15.2 转场动画优化
使用 CSS View Transitions API:
javascript复制function AnimatedRoutes() {
const location = useLocation()
useEffect(() => {
if (!document.startViewTransition) return
document.startViewTransition(() => {
// React 会在这里更新 DOM
})
}, [location.key])
return (
<Routes location={location}>
{/* 路由配置 */}
</Routes>
)
}
16. 路由监控与分析
16.1 用户导航追踪
javascript复制function useRouteTracker() {
const location = useLocation()
useEffect(() => {
analytics.track('page_view', {
path: location.pathname,
search: location.search
})
}, [location])
}
// 在根组件使用
function App() {
useRouteTracker()
// ...
}
16.2 性能指标收集
javascript复制function useRouteTiming() {
const location = useLocation()
const [timing, setTiming] = useState({})
useEffect(() => {
const start = performance.now()
return () => {
const end = performance.now()
setTiming(prev => ({
...prev,
[location.key]: end - start
}))
}
}, [location.key])
return timing
}
17. 服务端路由同构
17.1 数据预加载模式
javascript复制async function handleServerRequest(req, res) {
const url = req.url
const matches = matchRoutes(routes, url)
const promises = matches.map(match => {
const { route } = match
if (route.loadData) {
return route.loadData(match)
}
return Promise.resolve(null)
})
const data = await Promise.all(promises)
const context = { data }
const html = ReactDOMServer.renderToString(
<ServerApp location={url} context={context} />
)
res.send(`
<html>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(context)}
</script>
</body>
</html>
`)
}
17.2 路由级数据获取
javascript复制// 路由定义
{
path: '/products/:id',
element: <ProductPage />,
loadData: ({ params }) => fetchProduct(params.id)
}
// 客户端 hydrate
function hydrateClient() {
const data = window.__INITIAL_STATE__
ReactDOM.hydrate(
<App initialData={data} />,
document.getElementById('root')
)
}
18. 边缘计算路由处理
18.1 CDN 边缘路由
javascript复制// 边缘函数 (Cloudflare Workers 示例)
addEventListener('fetch', event => {
const url = new URL(event.request.url)
// 按路由规则处理
if (url.pathname.startsWith('/api')) {
return event.respondWith(handleAPI(event.request))
}
// 静态资源
if (url.pathname.startsWith('/static')) {
return event.respondWith(caches.match(event.request))
}
// 其他请求返回 SPA 入口
return event.respondWith(fetch('/index.html'))
})
18.2 路由级 A/B 测试
javascript复制// 边缘计算路由分流
function handleRequest(request) {
const cookie = request.headers.get('cookie')
const variant = cookie.includes('variant=b') ? 'b' : 'a'
const url = new URL(request.url)
if (url.pathname === '/') {
return fetch(variant === 'a' ? '/index-a.html' : '/index-b.html')
}
return fetch(request)
}
19. 路由安全加固
19.1 XSS 防护措施
javascript复制// 安全路由组件
function SafeRoute({ path, element }) {
const sanitizePath = (path) => {
// 移除可能的恶意字符
return path.replace(/[<>"']/g, '')
}
return <Route path={sanitizePath(path)} element={element} />
}
19.2 开放重定向防护
javascript复制function validateRedirect(url) {
const allowedDomains = ['example.com', 'trusted.org']
const target = new URL(url, window.location.origin)
if (!allowedDomains.includes(target.hostname)) {
throw new Error('非法重定向目标')
}
return url
}
// 安全重定向组件
function SafeNavigate({ to }) {
try {
const safeTo = validateRedirect(to)
return <Navigate to={safeTo} />
} catch {
return <Navigate to="/error" />
}
}
20. 路由设计模式
20.1 路由工厂模式
javascript复制function createCRUDRoutes(resource) {
return [
{
path: `/${resource}`,
element: <ListPage resource={resource} />
},
{
path: `/${resource}/:id`,
element: <DetailPage resource={resource} />
},
{
path: `/${resource}/new`,
element: <CreatePage resource={resource} />,
auth: true
}
]
}
// 使用
const productRoutes = createCRUDRoutes('products')
const userRoutes = createCRUDRoutes('users')
20.2 路由装饰器模式
javascript复制function withAnalytics(route) {
return {
...route,
element: (
<AnalyticsTracker event={route.path}>
{route.element}
</AnalyticsTracker>
)
}
}
// 使用
const routes = [
withAnalytics({
path: '/',
element: <Home />
})
]
21. 路由与 GraphQL 集成
21.1 路由级数据需求声明
javascript复制const routes = [
{
path: '/products/:id',
element: <ProductPage />,
query: gql`
query ProductDetails($id: ID!) {
product(id: $id) {
id
name
price
}
}
`,
variables: ({ params }) => ({ id: params.id })
}
]
21.2 数据预取策略
javascript复制function useRoutePrefetch() {
const router = useRouter()
const prefetch = (to) => {
const matches = router.matchRoutes(routes, to)
matches.forEach(({ route }) => {
if (route.query) {
client.query({
query: route.query,
variables: route.variables?.(match)
})
}
})
}
return prefetch
}
22. Web Components 路由集成
22.1 自定义元素路由
javascript复制class RouteElement extends HTMLElement {
connectedCallback() {
const path = this.getAttribute('path')
const template = this.querySelector('template')
window.addEventListener('popstate', () => {
this.toggleActive(window.location.pathname === path)
})
this.toggleActive(window.location.pathname === path)
}
toggleActive(active) {
this.style.display = active ? 'block' : 'none'
}
}
customElements.define('app-route', RouteElement)
22.2 混合式路由架构
javascript复制function ReactWrapper({ element }) {
const [isMounted, setIsMounted] = useState(false)
useEffect(() => {
setIsMounted(true)
}, [])
if (!isMounted) return null
return createPortal(
element,
document.querySelector('#react-root')
)
}
// Web Components 中使用 React 路由
<app-route path="/dashboard">
<template>
<react-wrapper>
<Dashboard />
</react-wrapper>
</template>
</app-route>
23. 路由测试策略进阶
23.1 路由变更测试工具
javascript复制function renderWithRouter(ui, { route = '/' } = {}) {
window.history.pushState({}, 'Test page', route)
return {
...render(ui, { wrapper: BrowserRouter }),
history: window.history
}
}
test('navigates to about page', async () => {
const { user } = renderWithRouter(<App />)
await user.click(screen.getByText('About'))
expect(window.location.pathname).toBe('/about')
})
23.2 路由拦截测试
javascript复制test('blocks navigation with unsaved changes', async () => {
const { user } = renderWithRouter(<FormWithBlocking />)
// 修改表单
await user.type(screen.getByLabelText('Name'), 'New Value')
// 尝试导航
await user.click(screen.getByText('Home'))
// 确认拦截
expect(screen.getByText('确认离开吗?')).toBeInTheDocument()
expect(window.location.pathname).not.toBe('/')
})
24. 路由性能优化进阶
24.1 智能预加载策略
javascript复制function useSmartPrefetch() {
const router = useRouter()
const [prefetched, setPrefetched] = useState(new Set())
const prefetch = useCallback((path) => {
if (prefetched.has(path)) return
// 根据网络连接类型调整预加载策略
const connection = navigator.connection || { effectiveType: '4g' }
if (connection.saveData || connection.effectiveType === '2g') {
return
}
router.prefetch(path)
setPrefetched(prev => new Set([...prev, path]))
}, [router, prefetched])
// 鼠标悬停预加载
const onLinkHover = useCallback((e) => {
const href = e.currentTarget.getAttribute('href')
if (href) prefetch(href)
}, [prefetch])
return { onLinkHover }
}
24.2 路由级代码分割优化
javascript复制// 动态导入优化配置
const ProductDetail = lazy(() => import(
/* webpackPreload: true */
/* webpackMagicComments: true */
/* webpackChunkName: "product-detail" */
'./pages/ProductDetail'
))
// Webpack 配置
module.exports = {
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
maxSize: 244000,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
25. 路由错误处理体系
25.1 错误边界集成
javascript复制function RouteErrorBoundary({ children }) {
const [error, setError] = useState(null)
const location = useLocation()
const resetError = useCallback(() => {
setError(null)
}, [])
useEffect(() => {
resetError()
}, [location.key, resetError])
if (error) {
return <ErrorFallback error={error} reset={resetError} />
}
return children
}
// 使用
<Route
path="/dashboard"
element={
<RouteErrorBoundary>
<Dashboard />
</RouteErrorBoundary>
}
/>
25.2 错误路由回退
javascript复制function ErrorRoutes() {
const location = useLocation()
const error = useRouteError()
if (isNotFoundError(error)) {
return <Route path="*" element={<NotFound />} />
}
if (isAuthError(error)) {
return (
<Route
path={location.pathname}
element={<Navigate to="/login" state={{ from: location }} />}
/>
)
}
return <Route path="*" element={<ErrorPage error={error} />} />
}
26. 路由与 Web Workers
26.1 路由状态共享
javascript复制// worker.js
self.addEventListener('message', (e) => {
if (e.data.type === 'NAVIGATE') {
// 处理路由逻辑
self.postMessage({ type: 'ROUTE_CHANGE', path: e.data.path })
}
})
// 主线程
const worker = new Worker('worker.js')
worker.onmessage = (e) => {
if (e.data.type === 'ROUTE_CHANGE') {
navigate(e.data.path)
}
}
// 导航时
worker.postMessage({ type: 'NAVIGATE', path: '/new-path' })
26.2 路由预加载 Worker
javascript复制function createPrefetchWorker() {
const worker = new Worker('prefetch.worker.js')
const prefetch = (path) => {
worker.postMessage({ type: 'PREFETCH', path })
}
return { prefetch }
}
// worker 中
self.importScripts('route-map.js')
self.addEventListener('message', (e) => {
if (e.data.type === 'PREFETCH') {
const route = findRoute(e.data.path)
if (route) {
importScripts(route.chunkUrl)
}
}
})
27. 路由与 WebAssembly
27.1 高性能路由匹配
rust复制// 使用 Rust 编写路由匹配逻辑
#[wasm_bindgen]
pub fn match_route(path: &str, pattern: &str) -> bool {
let re = Regex::new(&format!("^{}$", pattern.replace(":", "[^/]+"))).unwrap();
re.is_match(path)
}
27.2 路由级 WASM 加载
javascript复制const routes = [
{
path: '/image-editor',
element: <ImageEditor />,
wasm: '/wasm/image_editor_b