React-Router 原理与工作方式
React-Router 的路由模式、实现原理和简易实现
问题
React-Router 的实现原理及工作方式分别是什么?
解答
React-Router 基于两种路由模式实现:Hash 模式和 History 模式。它通过监听 URL 变化,匹配对应的路由配置,渲染相应组件。
两种路由模式
1. Hash 模式
URL 中带 #,如 http://example.com/#/home。通过监听 hashchange 事件实现。
// Hash 模式原理
window.addEventListener('hashchange', () => {
const hash = window.location.hash.slice(1) // 去掉 #
console.log('当前路径:', hash)
})
// 改变 hash
window.location.hash = '/about'
2. History 模式
URL 无 #,如 http://example.com/home。使用 HTML5 History API 实现。
// History 模式原理
window.addEventListener('popstate', (event) => {
console.log('当前路径:', window.location.pathname)
})
// 跳转页面(不刷新)
history.pushState({ page: 'about' }, '', '/about')
// 替换当前记录
history.replaceState({ page: 'home' }, '', '/home')
简易 Router 实现
import React, { createContext, useContext, useState, useEffect } from 'react'
// 创建路由上下文
const RouterContext = createContext()
// BrowserRouter 组件
function BrowserRouter({ children }) {
const [pathname, setPathname] = useState(window.location.pathname)
useEffect(() => {
// 监听浏览器前进后退
const handlePopState = () => {
setPathname(window.location.pathname)
}
window.addEventListener('popstate', handlePopState)
return () => window.removeEventListener('popstate', handlePopState)
}, [])
// 导航函数
const navigate = (to) => {
history.pushState(null, '', to)
setPathname(to)
}
return (
<RouterContext.Provider value={{ pathname, navigate }}>
{children}
</RouterContext.Provider>
)
}
// Route 组件
function Route({ path, element }) {
const { pathname } = useContext(RouterContext)
// 简单匹配,实际需要更复杂的匹配逻辑
return pathname === path ? element : null
}
// Routes 组件
function Routes({ children }) {
const { pathname } = useContext(RouterContext)
// 遍历子元素找到匹配的路由
let match = null
React.Children.forEach(children, (child) => {
if (!match && child.props.path === pathname) {
match = child
}
})
return match
}
// Link 组件
function Link({ to, children }) {
const { navigate } = useContext(RouterContext)
const handleClick = (e) => {
e.preventDefault() // 阻止默认跳转
navigate(to)
}
return <a href={to} onClick={handleClick}>{children}</a>
}
// useNavigate Hook
function useNavigate() {
const { navigate } = useContext(RouterContext)
return navigate
}
// 使用示例
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
)
}
路由匹配原理
React-Router v6 使用路径评分机制匹配路由:
// 简化的路径匹配函数
function matchPath(pattern, pathname) {
// 将路由模式转为正则
// /users/:id -> /users/([^/]+)
const regexPattern = pattern
.replace(/:\w+/g, '([^/]+)') // 动态参数
.replace(/\*/g, '.*') // 通配符
const regex = new RegExp(`^${regexPattern}$`)
const match = pathname.match(regex)
if (!match) return null
// 提取参数
const paramNames = [...pattern.matchAll(/:(\w+)/g)].map(m => m[1])
const params = {}
paramNames.forEach((name, index) => {
params[name] = match[index + 1]
})
return { params }
}
// 测试
matchPath('/users/:id', '/users/123')
// { params: { id: '123' } }
工作流程
URL 变化
↓
监听事件 (popstate / hashchange)
↓
更新 RouterContext 中的 pathname
↓
触发组件重新渲染
↓
Routes 遍历子 Route,匹配 path
↓
渲染匹配的组件
关键点
- Hash 模式:URL 带
#,兼容性好,通过hashchange事件监听 - History 模式:URL 无
#,需要服务器配置支持,通过popstate事件监听 - Context 传递:使用 React Context 在组件树中传递路由状态和导航方法
- Link 组件:阻止
<a>默认行为,使用pushState实现无刷新跳转 - 路由匹配:将路径模式转为正则表达式,支持动态参数和通配符
目录