📚 前置知识
在深入前端路由之前,建议先阅读以下两篇文章:
🔍 一、路由的本质
1.1 什么是路由?
路由(Routing)的本质是 URL 与资源之间的映射关系。在传统多页应用(MPA)中,每次 URL 变化都会触发服务器返回新的 HTML 页面。而在单页应用(SPA)中,前端通过 JavaScript 动态更新页面内容,避免了重复加载整个页面。
1.2 前端路由的核心目标
- 感知 URL 变化:通过监听事件或 API 监控 URL 的变化。
- 动态渲染内容:根据 URL 路径匹配对应的组件或页面内容。
- 无刷新切换:保持用户体验流畅,减少页面加载时间。
🧩 二、Hash 模式路由
2.1 原理详解
Hash 模式利用 URL 中的 # 符号(哈希值)来实现路由。浏览器会忽略 # 后的内容,因此哈希值的变化不会导致页面刷新。
核心机制
- 监听
hashchange事件:当 URL 中的哈希值变化时触发。 - 获取哈希值:通过
location.hash获取当前哈希值。 - 动态渲染内容:根据哈希值匹配对应的组件并渲染。
手搓哈希路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hash 路由示例</title>
</head>
<body>
<ul>
<li><a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>
<!-- url变更后展示对应的代码片段 -->
<div id="root"></div>
<script>
const routes = [
{ path: '/', component: () => '<h1>首页页面</h1>' },
{ path: '/home', component: () => '<h1>首页页面</h1>' },
{ path: '/about', component: () => '<h1>关于页面</h1>' },
];
// 监听地址栏的变化
window.addEventListener('hashchange', () => {
// js中location对象代表当前的地址栏
const hash = location.hash.slice(1); // 去掉开头的 #
renderView(hash);
});
function renderView (url) {
const route = routes.find(route => route.path === url)
if (route) {
document.getElementById('root').innerHTML = route.component()
} else {
document.getElementById('root').innerHTML = '<h1>404 页面不存在</h1>'
}
}
// 页面加载时初始化
window.addEventListener('DOMContentLoaded', () => {
// 页面加载时,渲染当前的hash对应的内容
const hash = location.hash.slice(1) || '/'; // 默认显示首页
renderView(hash);
});
</script>
</body>
</html>
2.2 优点与缺点
| 优点 | 缺点 |
|---|---|
| 兼容性好(支持 IE9+) | URL 中包含 #,不够美观 |
| 无需服务器配置 | 不利于 SEO 优化 |
🔄 三、History 模式路由
3.1 原理详解
History 模式基于 HTML5 的 History API,通过 pushState 和 replaceState 方法修改 URL,同时监听 popstate 事件响应用户操作(如点击浏览器的前进/后退按钮)。
核心机制
- 修改 URL:使用
history.pushState()或history.replaceState()更新地址栏。 - 监听
popstate事件:当用户点击浏览器的前进/后退按钮时触发。 - 动态渲染内容:根据当前路径匹配对应的组件并渲染。
手搓History路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>History 路由示例</title>
</head>
<body>
<ul>
<li><a href="/home">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
<div id="root"></div>
<script>
const routes = [
{ path: '/', component: () => '<h1>首页页面</h1>' },
{ path: '/home', component: () => '<h1>首页页面</h1>' },
{ path: '/about', component: () => '<h1>关于页面</h1>' },
];
function onLoad () {
let linkList = document.querySelectorAll('a[href]')
console.log('找到链接数量:', linkList.length)
linkList.forEach(link => {
// 为每个按钮添加点击事件,并进行history路由的相关操作
link.addEventListener('click', (e) => {
e.preventDefault() // 阻止默认的跳转行为
history.pushState(null, '', link.getAttribute('href')) // 使用history API改变地址栏
console.log(location.pathname) // 打印当前的路径
renderView(location.pathname) // 渲染对应的视图
})
})
}
// 监听浏览器前进/后退
window.addEventListener('popstate', () => {
renderView(location.pathname);
});
// 渲染页面
function renderView(path) {
// 和hash模式一样,此处省略
}
// 页面首次加载时渲染
window.addEventListener('DOMContentLoaded', () => {
onLoad() // 初始化链接事件
});
</script>
</body>
</html>
3.2 优点与缺点
| 优点 | 缺点 |
|---|---|
URL 美观(无 #) | 需要服务器配置支持 |
| 更利于 SEO 优化 | 兼容性较差(IE9 及以下不支持) |
🧭 四、Hash 与 History 模式的对比
| 特性 | Hash 模式 | History 模式 |
|---|---|---|
| URL 结构 | example.com/#/home | example.com/home |
| 兼容性 | 支持所有浏览器 | 仅支持 HTML5 浏览器 |
| 服务器配置 | 无需特殊配置 | 需要配置所有路径返回 index.html |
| SEO 友好度 | 较差 | 更友好 |
| 典型使用场景 | 内部管理系统、静态网站部署 | 需要 SEO 优化的 Web 应用 |
🛠️ 五、实际应用中的选择建议
-
优先使用 History 模式:
- 如果服务器支持(如 Nginx 配置
try_files $uri /index.html)。 - 项目需要 SEO 优化(如电商网站、博客平台)。
- 如果服务器支持(如 Nginx 配置
-
选择 Hash 模式:
- 部署在传统服务器(如 GitHub Pages、CDN)。
- 不需要 SEO 优化(如企业内部管理系统)。
-
兼容性处理:
- 对于老旧浏览器(如 IE11),可回退到 Hash 模式。
🌟 六、总结
前端路由的本质是 通过 JavaScript 动态管理 URL 与页面内容的映射关系,其核心在于无刷新切换页面和动态渲染组件。Hash 模式与 History 模式各有优劣,开发者需根据项目需求(如 SEO、服务器配置、兼容性)选择合适的方案。
在实际开发中,推荐使用成熟的路由库(如 React Router、Vue Router),它们封装了底层逻辑,提供声明式 API 和丰富的功能(如嵌套路由、权限控制)。掌握前端路由的原理,不仅能帮助你更好地调试和优化应用,还能为构建高性能、现代化的 Web 应用打下坚实基础。