手写 Hash Router:从原理到实现
2025.09.19 12:47浏览量:0简介:本文深入解析Hash Router的核心原理,结合代码示例详细说明如何手动实现一个轻量级路由系统,涵盖监听机制、路由匹配、动态参数处理等关键环节。
一、Hash Router 的核心原理
Hash Router(哈希路由)是前端路由实现中最基础的一种方案,其核心原理依赖于URL的哈希部分(即#
之后的内容)的变化。当URL哈希改变时,浏览器不会重新加载页面,而是触发hashchange
事件,开发者可以通过监听该事件实现单页应用(SPA)的路由切换。
1.1 哈希值与浏览器行为
URL的哈希部分原本用于指定页面内的锚点位置,但现代前端框架利用其“无刷新”特性实现路由控制。例如:
https://example.com/#/home
https://example.com/#/profile
浏览器会加载同一HTML文件,但哈希值的差异可通过JavaScript捕获,进而动态更新页面内容。
1.2 哈希变化的触发方式
哈希值可通过以下方式改变:
- 用户点击链接:
<a href="#/about">About</a>
- 代码修改
location.hash
:window.location.hash = '/contact'
- 浏览器前进/后退:触发历史记录中的哈希变化
1.3 事件监听机制
通过window.addEventListener('hashchange', callback)
,开发者可在哈希变化时执行路由匹配逻辑。结合window.location.hash
可获取当前哈希值,解析后决定渲染哪个组件。
二、手写 Hash Router 的实现步骤
2.1 初始化路由容器与配置
首先需要定义路由表,存储路径与对应组件的映射关系:
class HashRouter {
constructor(options) {
this.routes = options.routes || {}; // 路由配置 { '/home': HomeComponent }
this.currentPath = '';
this.init();
}
init() {
// 监听hashchange事件
window.addEventListener('hashchange', this.onHashChange.bind(this));
// 初始路由匹配
this.onHashChange();
}
}
2.2 哈希变化处理逻辑
在onHashChange
方法中,需完成以下操作:
- 获取当前哈希值(去掉
#
前缀) - 匹配路由表中的组件
渲染对应组件到DOM容器
onHashChange() {
this.currentPath = window.location.hash.slice(1) || '/';
const Component = this.routes[this.currentPath];
if (Component) {
const container = document.getElementById('app');
container.innerHTML = '';
container.appendChild(new Component().render());
} else {
console.error('404: Route not found');
}
}
2.3 动态路由参数处理
实际场景中,路由可能包含动态参数(如/user/:id
)。需实现参数解析功能:
// 扩展路由配置
{
'/user/:id': UserComponent
}
// 解析逻辑
parsePath(path) {
const regex = /^\/user\/([^\/]+)$/;
const match = path.match(regex);
if (match) {
return { component: this.routes['/user/:id'], params: { id: match[1] } };
}
return null;
}
2.4 路由导航方法
提供编程式导航API,允许代码中触发路由跳转:
class HashRouter {
// ...其他代码
push(path) {
window.location.hash = path;
}
replace(path) {
const oldHash = window.location.hash;
window.location.hash = path;
// 替换历史记录需结合history.replaceState(需额外处理)
}
}
三、完整实现示例
class HashRouter {
constructor(options) {
this.routes = options.routes || {};
this.container = options.container || 'app';
this.currentPath = '';
this.init();
}
init() {
window.addEventListener('hashchange', this.onHashChange.bind(this));
this.onHashChange();
}
onHashChange() {
this.currentPath = window.location.hash.slice(1) || '/';
const result = this.parsePath(this.currentPath);
if (result) {
const { component: Component, params } = result;
document.getElementById(this.container).innerHTML = '';
const instance = new Component(params);
document.getElementById(this.container).appendChild(instance.render());
} else {
console.error('404: Route not found');
}
}
parsePath(path) {
for (const [routePattern, Component] of Object.entries(this.routes)) {
const params = {};
const regexStr = routePattern
.replace(/\/:([^\/]+)/g, (match, paramName) => {
params[paramName] = true;
return '/([^/]+)';
});
const regex = new RegExp(`^${regexStr}$`);
const match = path.match(regex);
if (match) {
const actualParams = {};
Object.keys(params).forEach((key, index) => {
actualParams[key] = match[index + 1];
});
return { component: Component, params: actualParams };
}
}
return null;
}
push(path) {
window.location.hash = path;
}
}
// 使用示例
class Home {
render() {
const div = document.createElement('div');
div.textContent = 'Home Page';
return div;
}
}
class User {
constructor(params) {
this.id = params.id;
}
render() {
const div = document.createElement('div');
div.textContent = `User ID: ${this.id}`;
return div;
}
}
const router = new HashRouter({
routes: {
'/': Home,
'/user/:id': User
},
container: 'app'
});
// 触发路由
router.push('/user/123');
四、优化与扩展方向
- 路由守卫:添加
beforeEach
和afterEach
钩子实现权限控制 - 懒加载:结合动态
import()
实现组件按需加载 - 嵌套路由:支持多级路由结构
- 哈希模式兼容性:处理IE等旧浏览器的
onhashchange
兼容问题 - TypeScript支持:添加类型定义提升代码可维护性
五、实际应用建议
- 小型项目首选:Hash Router实现简单,适合原型开发或轻量级应用
- 结合构建工具:使用Webpack/Vite等工具的HTML5 History API fallback配置
- 服务端配合:确保服务端对所有路由返回同一HTML文件
- 性能监控:通过
performance.navigation
检测路由切换耗时
通过手写Hash Router,开发者不仅能深入理解前端路由机制,还能根据项目需求定制化功能。这种实现方式在面试场景中常被用作考察对前端工程化的理解深度,同时也是学习React Router/Vue Router等库的绝佳起点。
发表评论
登录后可评论,请前往 登录 或 注册