手写 Hash Router:从原理到实现
2025.09.19 12:47浏览量:5简介:本文深入解析Hash Router的核心原理,结合代码示例详细说明如何手动实现一个轻量级路由系统,涵盖监听机制、路由匹配、动态参数处理等关键环节。
一、Hash Router 的核心原理
Hash Router(哈希路由)是前端路由实现中最基础的一种方案,其核心原理依赖于URL的哈希部分(即#之后的内容)的变化。当URL哈希改变时,浏览器不会重新加载页面,而是触发hashchange事件,开发者可以通过监听该事件实现单页应用(SPA)的路由切换。
1.1 哈希值与浏览器行为
URL的哈希部分原本用于指定页面内的锚点位置,但现代前端框架利用其“无刷新”特性实现路由控制。例如:
https://example.com/#/homehttps://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等库的绝佳起点。

发表评论
登录后可评论,请前往 登录 或 注册