📖 一、引言
在现代前端开发中,路由(Route) 是一个核心概念。它决定了用户访问不同 URL 时,浏览器应该展示什么内容。理解路由的演进过程,是掌握前端框架(如 Vue、React)路由机制的基础。
本文将从最基础的浏览器 URL 讲起,逐步带你理解:
- 🌐 浏览器 URL 的本质
- 📄 传统多页应用的工作方式
- ⚡ 单页应用(SPA)的诞生背景
- 🔀 Hash 路由的实现原理
🌐 二、浏览器 URL 是什么?
当我们通过 navigator 对象在浏览器地址栏输入一个 URL 时,会发生以下过程:
用户输入 URL → 浏览器发起 HTTP 请求 → 服务器响应 text/html → 浏览器渲染页面 → 浏览器历史插入一条记录
💡 关键点 :每一次请求,服务器返回完整的 HTML 文档,浏览器全量渲染整个页面,同时在浏览历史中记录一条新条目。
这就是传统 Web 应用的工作模式------万物互联,靠的是链接。
html
<a href="https://www.baidu.com">百度一下</a>
点击这个链接,浏览器就会发起一次全新的页面请求,整个页面被替换。
📄 三、传统多页应用(MPA)
3.1 工作方式
传统多页应用中,每个页面都是一个独立的 HTML 文件:
html
<!-- index.html -->
<header>
<nav>
<ul>
<li><a href="/index.html">首页</a></li>
<li><a href="/about.html">关于我们</a></li>
</ul>
</nav>
</header>
<main>
<h1>首页</h1>
</main>
html
<!-- about.html -->
<header>
<nav>
<ul>
<li><a href="/index.html">首页</a></li>
<li><a href="/about.html">关于我们</a></li>
</ul>
</nav>
</header>
<main>
<h1>关于我们</h1>
</main>
3.2 存在的问题 🤔
| 问题 | 说明 |
|---|---|
| 重复渲染 | 每次跳转都要重新加载整个页面,包括 header、nav 等公共部分 |
| 白屏闪烁 | 网速稍慢时,页面切换会出现明显的白屏 |
| 资源浪费 | 公共部分被反复请求和渲染,浪费带宽和性能 |
| 体验不佳 | 在移动端,这种"闪一下"的体验与原生 App 差距明显 |
📱 移动时代的要求:用户期望的是像原生 App 一样流畅的体验,而不是每次都"闪一下"。
⚛️ 四、单页应用(SPA)的诞生
4.1 什么是 SPA?
SPA(Single Page Application) 即单页应用。它的核心思想是:
🎯 只加载一个 HTML 页面,通过 JavaScript 动态替换页面内容,实现"无刷新"跳转。
URL 变化 → 前端拦截 → JS 动态渲染对应内容 → 页面无刷新更新
4.2 实现思路
传统方式中,URL 和资源是一一对应的:
/index.html→ 首页内容/about.html→ 关于页面
在 SPA 中,我们需要解决一个核心问题:
❓ 如何在改变 URL 的同时,不让页面刷新?
答案就是------Hash 路由。
🔀 五、Hash 路由
5.1 URL 的结构
先来认识一下 URL 的完整结构:
https://www.example.com/user/123?a=1&b=2#/page1
↑协议 ↑主机 ↑路径 ↑查询参数 ↑Hash
其中,Hash(哈希) 部分以 # 开头,它是 URL 的一部分,但有一个神奇的特性:
✨ 修改 URL 的 Hash 部分,页面不会刷新!
5.2 锚链接回顾
Hash 最初的用途是锚链接------在长页面中快速跳转到指定位置:
html
<!-- 锚链接示例 -->
<a name="top"></a>
<a href="#bottom">跳转到底部</a>
<div style="height: 200vh; background-color: yellow;"></div>
<a href="#top">回到顶部</a>
<div style="height: 300vh; background-color: red;"></div>
<a name="bottom"></a>
点击"跳转到底部",页面会平滑滚动到底部区域,URL 会变成 #bottom,但页面不会刷新。
5.3 监听 Hash 变化
浏览器提供了 hashchange 事件,可以监听 Hash 的变化:
javascript
window.addEventListener('hashchange', function (event) {
console.log('hash 改变了');
console.log('新的 URL:', event.newURL);
console.log('旧的 URL:', event.oldURL);
});
💡 这就是前端路由的核心机制:监听 URL Hash 的变化,根据不同的 Hash 值,渲染不同的页面内容。
🛠️ 六、手写一个 Hash 路由
下面我们来实现一个简单但完整的 HashRouter 类:
6.1 HTML 结构
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPA Hash Router</title>
</head>
<body>
<header>
<nav>
<ul>
<li><a href="#/page1">页面一</a></li>
<li><a href="#/page2">页面二</a></li>
<li><a href="#/page3">页面三</a></li>
</ul>
</nav>
</header>
<div id="container"></div>
<script>
// JavaScript 代码见下文
</script>
</body>
</html>
6.2 HashRouter 类实现
javascript
class HashRouter {
constructor() {
// 路由配置表:存储 hash -> callback 的映射关系
this.routes = {};
// 监听 hashchange 事件
// 注意:使用 bind 绑定 this,确保 load 方法中的 this 指向实例
window.addEventListener('hashchange', this.load.bind(this));
}
/**
* 注册路由
* @param {string} hash - 路由路径,如 '/page1'
* @param {Function} callback - 对应的渲染函数
*/
register(hash, callback) {
this.routes[hash] = callback;
}
/**
* 加载路由:根据当前 hash 值执行对应的回调
*/
load() {
// 获取当前 hash,并去掉开头的 '#'
let hash = location.hash.slice(1);
if (hash) {
let handle = this.routes[hash];
if (handle) {
handle.call(this);
}
}
}
}
6.3 注册路由并使用
javascript
// 创建路由实例
let router = new HashRouter();
// 获取页面挂载点
let container = document.getElementById('container');
// 注册路由
router.register('/page1', () => {
container.innerHTML = '<h1>页面一</h1><p>这是页面一的内容</p>';
});
router.register('/page2', () => {
container.innerHTML = '<h1>页面二</h1><p>这是页面二的内容</p>';
});
router.register('/page3', () => {
container.innerHTML = '<h1>页面三</h1><p>这是页面三的内容</p>';
});
6.4 运行效果
用户点击 "页面一"
↓
URL 变为 #/page1(页面不刷新)
↓
触发 hashchange 事件
↓
执行 load() 方法
↓
查找 routes['/page1']
↓
执行回调,更新 #container 的内容
↓
✅ 页面内容无缝切换!
🎯 七、核心要点总结
| 要点 | 说明 |
|---|---|
| 📍 URL 与资源一一对应 | 不同的 URL 应展示不同的内容 |
| 🔀 Hash 改变不会刷新页面 | 这是实现前端路由的关键特性 |
| 📡 hashchange 事件 | 浏览器原生提供的 Hash 变化监听机制 |
| 🧩 this 绑定 | 事件回调中需要注意 this 的指向问题 |
| 🏗️ 路由注册机制 | 通过 routes 对象建立 hash → callback 的映射 |
🚀 八、总结
从传统多页应用到单页应用,前端路由的演进解决了以下核心问题:
- ✅ 消除白屏闪烁 --- 页面切换无需刷新
- ✅ 提升用户体验 --- 接近原生 App 的流畅感
- ✅ URL 与内容对应 --- 保持可分享、可收藏的 URL 特性
- ✅ 按需渲染 --- 只更新变化的部分,节省资源
🎉 Hash 路由是前端路由的起点,理解了它,再去学习 Vue Router、React Router 等框架的路由实现,就会轻松很多!
📝 本文代码均已在浏览器中测试通过,可直接运行学习。