文章目录
-
- [1. React Router 简介](#1. React Router 简介)
-
- [1.1 安装 React Router](#1.1 安装 React Router)
- [1.2 基本使用](#1.2 基本使用)
- [2. HashRouter 模式](#2. HashRouter 模式)
-
- [2.1 什么是 HashRouter](#2.1 什么是 HashRouter)
- [2.2 HashRouter 实现原理](#2.2 HashRouter 实现原理)
-
- [2.2.1 核心机制](#2.2.1 核心机制)
- [2.2.2 React Router 中的 HashRouter 实现](#2.2.2 React Router 中的 HashRouter 实现)
- [2.3 HashRouter 使用示例](#2.3 HashRouter 使用示例)
- [2.4 HashRouter 流程图](#2.4 HashRouter 流程图)
- [3. BrowserRouter 模式](#3. BrowserRouter 模式)
-
- [3.1 什么是 BrowserRouter](#3.1 什么是 BrowserRouter)
- [3.2 BrowserRouter 实现原理](#3.2 BrowserRouter 实现原理)
-
- [3.2.1 History API 核心方法](#3.2.1 History API 核心方法)
- [3.2.2 React Router 中的 BrowserRouter 实现](#3.2.2 React Router 中的 BrowserRouter 实现)
- [3.3 BrowserRouter 使用示例](#3.3 BrowserRouter 使用示例)
- [3.4 BrowserRouter 流程图](#3.4 BrowserRouter 流程图)
- [4. 两种模式的对比](#4. 两种模式的对比)
-
- [4.1 功能特性对比](#4.1 功能特性对比)
- [4.2 服务器配置要求](#4.2 服务器配置要求)
-
- [HashRouter 服务器配置](#HashRouter 服务器配置)
- [BrowserRouter 服务器配置](#BrowserRouter 服务器配置)
- [4.3 选择建议](#4.3 选择建议)
- [5. 实际应用示例](#5. 实际应用示例)
-
- [5.1 动态路由应用](#5.1 动态路由应用)
- [5.2 路由守卫示例](#5.2 路由守卫示例)
- [6. 总结](#6. 总结)
在现代前端单页面应用(SPA)开发中,路由管理是至关重要的一环。React Router 作为 React 生态中最流行的路由库,提供了两种主要的路由模式:HashRouter 和 BrowserRouter。本文将深入探讨这两种模式的实现原理、使用场景和差异。
1. React Router 简介
React Router 是 React 官方推荐的路由库,它通过管理 URL 与组件之间的映射关系,实现了单页面应用的多视图切换功能。目前 React Router 已发展到 v6 版本,提供了更加简洁和强大的 API。
1.1 安装 React Router
bash
npm install react-router-dom
1.2 基本使用
jsx
import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/contact">联系</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
export default App;
2. HashRouter 模式
2.1 什么是 HashRouter
HashRouter 使用 URL 的 hash 部分(即 # 号后面的内容)来管理路由。这种模式兼容性最好,可以在所有浏览器中运行,并且不需要服务器端配置。
示例 URL:
http://example.com/#/home
http://example.com/#/about
http://example.com/#/users/123
2.2 HashRouter 实现原理
2.2.1 核心机制
HashRouter 的核心原理是利用 window.location.hash 属性和 hashchange 事件:
javascript
class SimpleHashRouter {
constructor() {
this.routes = {};
this.currentUrl = '';
// 监听 hashchange 事件
window.addEventListener('hashchange', this.refresh.bind(this), false);
window.addEventListener('load', this.refresh.bind(this), false);
}
// 注册路由
route(path, callback) {
this.routes[path] = callback || function() {};
}
// 路由刷新
refresh() {
this.currentUrl = window.location.hash.slice(1) || '/';
if (this.routes[this.currentUrl]) {
this.routes[this.currentUrl]();
}
}
// 导航到新路由
navigate(path) {
window.location.hash = '#' + path;
}
}
// 使用示例
const router = new SimpleHashRouter();
router.route('/', () => {
document.getElementById('content').innerHTML = '<h1>首页</h1>';
});
router.route('/about', () => {
document.getElementById('content').innerHTML = '<h1>关于我们</h1>';
});
2.2.2 React Router 中的 HashRouter 实现
React Router 的 HashRouter 组件内部实现更加复杂,但基本原理相同:
jsx
import React, { useState, useEffect } from 'react';
import { Router } from 'react-router-dom';
function HashRouter({ children }) {
const [location, setLocation] = useState({
pathname: window.location.hash.slice(1) || '/',
search: '',
hash: '',
state: null
});
useEffect(() => {
const handleHashChange = () => {
setLocation(prev => ({
...prev,
pathname: window.location.hash.slice(1) || '/'
}));
};
window.addEventListener('hashchange', handleHashChange);
return () => {
window.removeEventListener('hashchange', handleHashChange);
};
}, []);
const history = {
push: (path) => {
window.location.hash = '#' + path;
},
replace: (path) => {
window.location.replace('#' + path);
},
go: (n) => {
window.history.go(n);
},
goBack: () => {
window.history.back();
},
goForward: () => {
window.history.forward();
},
location
};
return (
<Router history={history} location={location}>
{children}
</Router>
);
}
2.3 HashRouter 使用示例
jsx
import React from 'react';
import { HashRouter, Routes, Route, Link, useNavigate } from 'react-router-dom';
function Home() {
const navigate = useNavigate();
return (
<div>
<h1>首页</h1>
<button onClick={() => navigate('/about')}>跳转到关于页面</button>
</div>
);
}
function About() {
return <h1>关于我们</h1>;
}
function User({ id }) {
return <h1>用户详情 - ID: {id}</h1>;
}
function App() {
return (
<HashRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/user/123">用户123</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
</Routes>
</HashRouter>
);
}
export default App;
2.4 HashRouter 流程图
用户点击链接或调用 navigate 更新 window.location.hash 触发 hashchange 事件 React Router 监听器捕获事件 更新 Router 内部状态 重新渲染匹配的组件 页面内容更新
3. BrowserRouter 模式
3.1 什么是 BrowserRouter
BrowserRouter 使用 HTML5 History API 来管理路由,创建的是真实的 URL 路径,不包含 # 符号。这种模式创建的 URL 更加美观,更符合用户习惯。
示例 URL:
http://example.com/home
http://example.com/about
http://example.com/users/123
3.2 BrowserRouter 实现原理
3.2.1 History API 核心方法
BrowserRouter 依赖于 HTML5 History API,主要方法包括:
history.pushState(): 添加新的历史记录history.replaceState(): 替换当前历史记录popstate事件: 当用户导航历史记录时触发
javascript
class SimpleBrowserRouter {
constructor() {
this.routes = {};
this.currentUrl = '';
// 监听 popstate 事件
window.addEventListener('popstate', this.refresh.bind(this), false);
window.addEventListener('load', this.refresh.bind(this), false);
}
// 注册路由
route(path, callback) {
this.routes[path] = callback || function() {};
}
// 路由刷新
refresh() {
this.currentUrl = window.location.pathname || '/';
if (this.routes[this.currentUrl]) {
this.routes[this.currentUrl]();
}
}
// 导航到新路由
push(path) {
window.history.pushState(null, null, path);
this.refresh();
}
replace(path) {
window.history.replaceState(null, null, path);
this.refresh();
}
}
// 使用示例
const router = new SimpleBrowserRouter();
router.route('/', () => {
document.getElementById('content').innerHTML = '<h1>首页</h1>';
});
router.route('/about', () => {
document.getElementById('content').innerHTML = '<h1>关于我们</h1>';
});
3.2.2 React Router 中的 BrowserRouter 实现
React Router 的 BrowserRouter 组件实现:
jsx
import React, { useState, useEffect } from 'react';
import { Router } from 'react-router-dom';
function BrowserRouter({ children }) {
const [location, setLocation] = useState({
pathname: window.location.pathname,
search: window.location.search,
hash: window.location.hash,
state: null
});
useEffect(() => {
const handlePopState = () => {
setLocation({
pathname: window.location.pathname,
search: window.location.search,
hash: window.location.hash,
state: window.history.state
});
};
window.addEventListener('popstate', handlePopState);
return () => {
window.removeEventListener('popstate', handlePopState);
};
}, []);
const history = {
push: (path, state) => {
window.history.pushState(state, '', path);
setLocation({
pathname: window.location.pathname,
search: window.location.search,
hash: window.location.hash,
state
});
},
replace: (path, state) => {
window.history.replaceState(state, '', path);
setLocation({
pathname: window.location.pathname,
search: window.location.search,
hash: window.location.hash,
state
});
},
go: (n) => {
window.history.go(n);
},
goBack: () => {
window.history.back();
},
goForward: () => {
window.history.forward();
},
location
};
return (
<Router history={history} location={location}>
{children}
</Router>
);
}
3.3 BrowserRouter 使用示例
jsx
import React from 'react';
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';
function Home() {
return <h1>首页</h1>;
}
function About() {
return <h1>关于我们</h1>;
}
function User() {
const { id } = useParams();
return <h1>用户详情 - ID: {id}</h1>;
}
function NotFound() {
return <h1>404 - 页面未找到</h1>;
}
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/user/123">用户123</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
export default App;
3.4 BrowserRouter 流程图
用户点击链接或调用 navigate 调用 history.pushState 更新 URL 但不刷新页面 React Router 更新内部状态 重新渲染匹配的组件 页面内容更新 用户点击浏览器前进/后退 触发 popstate 事件 React Router 监听器捕获事件 更新 Router 内部状态 重新渲染匹配的组件 页面内容更新
4. 两种模式的对比
4.1 功能特性对比
| 特性 | HashRouter | BrowserRouter |
|---|---|---|
| URL 美观度 | 较差(包含 #) | 较好(纯路径) |
| 兼容性 | 所有浏览器 | IE10+ |
| 服务器配置 | 不需要 | 需要配置支持 |
| SEO 友好性 | 较差 | 较好 |
| 实现原理 | hashchange 事件 | History API |
4.2 服务器配置要求
HashRouter 服务器配置
HashRouter 不需要特殊服务器配置,因为 # 后面的内容不会发送到服务器。
BrowserRouter 服务器配置
BrowserRouter 需要服务器配置,确保所有路由都返回 index.html:
Express 服务器配置:
javascript
const express = require('express');
const path = require('path');
const app = express();
// 静态文件服务
app.use(express.static(path.join(__dirname, 'build')));
// 所有路由都返回 index.html
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(9000);
Nginx 服务器配置:
nginx
server {
listen 80;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
4.3 选择建议
-
使用 HashRouter 的情况:
- 静态网站托管(如 GitHub Pages)
- 不支持 History API 的旧浏览器
- 没有服务器配置权限
- 快速原型开发
-
使用 BrowserRouter 的情况:
- 有自己的服务器并可以配置
- 需要 SEO 友好的 URL
- 现代浏览器环境
- 生产环境应用
5. 实际应用示例
5.1 动态路由应用
jsx
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';
// 模拟数据获取
const fetchUser = (id) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
name: `用户 ${id}`,
email: `user${id}@example.com`
});
}, 500);
});
};
function UserList() {
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
];
return (
<div>
<h1>用户列表</h1>
<ul>
{users.map(user => (
<li key={user.id}>
<Link to={`/user/${user.id}`}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
function UserDetail() {
const { id } = useParams();
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadUser = async () => {
setLoading(true);
const userData = await fetchUser(id);
setUser(userData);
setLoading(false);
};
loadUser();
}, [id]);
if (loading) return <div>加载中...</div>;
if (!user) return <div>用户不存在</div>;
return (
<div>
<h1>用户详情</h1>
<p><strong>ID:</strong> {user.id}</p>
<p><strong>姓名:</strong> {user.name}</p>
<p><strong>邮箱:</strong> {user.email}</p>
</div>
);
}
function App() {
return (
<BrowserRouter>
<nav style={{ padding: '10px', borderBottom: '1px solid #ccc' }}>
<Link to="/" style={{ marginRight: '10px' }}>首页</Link>
<Link to="/users">用户列表</Link>
</nav>
<div style={{ padding: '20px' }}>
<Routes>
<Route path="/" element={<h1>欢迎来到用户管理系统</h1>} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:id" element={<UserDetail />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
5.2 路由守卫示例
jsx
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom';
// 模拟认证状态
const useAuth = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const login = () => {
setIsAuthenticated(true);
localStorage.setItem('isAuthenticated', 'true');
};
const logout = () => {
setIsAuthenticated(false);
localStorage.removeItem('isAuthenticated');
};
useEffect(() => {
const authStatus = localStorage.getItem('isAuthenticated');
if (authStatus === 'true') {
setIsAuthenticated(true);
}
}, []);
return { isAuthenticated, login, logout };
};
// 路由守卫组件
function ProtectedRoute({ children }) {
const { isAuthenticated } = useAuth();
const location = useLocation();
if (!isAuthenticated) {
// 重定向到登录页,并保存当前路径以便登录后返回
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
function LoginPage() {
const { login } = useAuth();
const location = useLocation();
const from = location.state?.from?.pathname || '/';
const handleLogin = () => {
login();
// 登录后跳转到之前尝试访问的页面或首页
window.location.href = from;
};
return (
<div>
<h1>登录页面</h1>
<button onClick={handleLogin}>登录</button>
</div>
);
}
function Dashboard() {
const { logout } = useAuth();
return (
<div>
<h1>仪表板</h1>
<p>这是受保护的页面</p>
<button onClick={logout}>退出登录</button>
</div>
);
}
function PublicPage() {
return <h1>公开页面</h1>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<PublicPage />} />
<Route path="/login" element={<LoginPage />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
);
}
export default App;
6. 总结
React Router 提供了两种主要的路由模式:HashRouter 和 BrowserRouter,它们各有优缺点和适用场景。
- HashRouter 基于 URL hash 和 hashchange 事件,兼容性好,无需服务器配置,适合静态托管和简单应用。
- BrowserRouter 基于 HTML5 History API,URL 美观,SEO 友好,但需要服务器配置,适合现代浏览器和生产环境应用。
在实际开发中,应根据项目需求、目标用户浏览器环境和服务器配置能力来选择合适的路由模式。对于大多数现代 Web 应用,BrowserRouter 是更好的选择,因为它提供了更好的用户体验和开发体验。
无论选择哪种模式,React Router 都提供了强大而灵活的路由管理能力,可以帮助开发者构建复杂的单页面应用程序。