手撕Hash路由:从面试翻车到源码王者之路 🚀

引言

程序员面对屏幕抓狂

一、那个让我血压升高的面试现场 ⚡

"小明同学,能讲讲前端路由的实现原理吗?" 面试官推了推金丝眼镜,嘴角微微上扬。

我表面稳如老狗,内心慌得一批:"路由嘛,就是...那个...通过监听URL变化加载不同内容?"

"那手写一个Hash路由试试?" 面试官的笑容逐渐变态。

此刻我的CPU开始疯狂运转:Hash路由的组成要素是啥?事件监听怎么绑定?路由表用对象还是数组?脑子里突然蹦出曾经背过的八股文知识点,却像被猫玩乱的毛线球一样纠缠不清... 😿

三十分钟后,我交出了一份漏洞百出的代码,面试官的表情仿佛在说"就这?"。这次惨痛经历让我痛定思痛------是时候彻底征服这个前端必考题了!


背景知识

零、先来补个课:什么是哈希路由?🔍

在开始前可能有部分同学不知道哈希路由是什么? 我们可以来到掘金打开某位大佬的文章: 看到右边的目录:


我们可以试着点一点这各个模块

我们可以看到这上面的路由从

变成了:

这个#/*******就是我们说的哈希值 (Hash),也叫锚点。它最初的设计是用来跳转到页面内的特定位置(就像书签一样),但是!前端大佬们发现了它的隐藏用法:

神奇特性

修改#后面的内容不会触发页面刷新 !(但会新增历史记录)

这意味着我们可以通过监听这个值的变化,实现无刷新页面切换------这就是哈希路由的核心魔法!✨


二、解剖Hash路由:原来你是这样的变色龙 🦎

2.1 为什么选择Hash路由?

先来点硬核知识开胃菜:

  1. 兼容性之王:支持IE8等上古浏览器(虽然现在用的人不多)
  2. SPA救星:改变hash不会触发页面刷新
  3. 监控简单 :一个hashchange事件全搞定
  4. 服务端无痛:不需要服务端特殊配置

2.2 核心实现原理脑图

graph TD A[初始化路由表] --> B[监听hashchange事件] B --> C{当前hash是否存在} C -->|存在| D[执行对应回调] C -->|不存在| E[执行默认回调或404]

三、手把手实现:从青铜到王者的进化之路 👑

3.1 基础版代码实现(含逐行解析)

javascript 复制代码
class HashRouter {
    constructor() {
        this.routes = {}; // 路由登记簿
        window.addEventListener('load', this.load.bind(this)); // 首次加载监听
        window.addEventListener('hashchange', this.load.bind(this)); // 哈希变化监听
    }

    // 注册普通路由
    register(path, callback = () => {}) {
        this.routes[path] = callback; // 把路由存入"通讯录"
    }

    // 注册首页
    registerIndex(callback = () => {}) {
        this.routes['/index'] = callback; // 特殊待遇的首页
    }

    // 核心调度方法
    load() {
        const hash = location.hash.slice(1); // 去掉#号
        let handler;
        
        if (!hash) { // 空路径走首页
            handler = this.routes['/index'];
        } else if (this.routes[hash]) { // 正常路由
            handler = this.routes[hash];
        } else { // 404处理
            handler = () => { container.innerHTML = '404 页面走丢了!' };
        }
        
        handler.call(this); // 执行对应回调
    }
}

关键点解析

  1. location.hash.slice(1):获取#后的路径,比如#/page1得到/page1
  2. 双保险的事件监听:页面加载时和hash变化时都要触发
  3. 路由表使用对象存储:实现O(1)时间复杂度查找

实现效果

3.2 常见翻车现场 🚑

坑1:this指向问题

javascript 复制代码
// 错误示范
window.addEventListener('hashchange', this.load); // this会指向window!

// 正确姿势
window.addEventListener('hashchange', this.load.bind(this));

坑2:重复注册问题

javascript 复制代码
// 如果多次注册同一个路由...
router.register('/page1', () => console.log('第一次'));
router.register('/page1', () => console.log('第二次'));

// 解决方案:注册前先检查
register(path, callback) {
    if(this.routes[path]) {
        console.warn(`重复注册路由:${path}`);
    }
    this.routes[path] = callback;
}

四、给路由加点黑科技 🔥

4.1 动态路由支持

javascript 复制代码
// 支持形如 /user/:id 的路由
register('/user/:id', (params) => {
    console.log(`用户ID:${params.id}`);
});

// 在load方法中增加路径解析
const match = Object.keys(this.routes).find(key => {
    const routePath = key.replace(/:\w+/g, '\\w+');
    return new RegExp(`^${routePath}$`).test(hash);
});

4.2 路由守卫

javascript 复制代码
class HashRouter {
    constructor() {
        this.beforeHooks = [];
    }

    beforeEach(callback) {
        this.beforeHooks.push(callback);
    }

    load() {
        // 执行守卫钩子
        const next = () => {
            // 原有逻辑...
        };
        this.runQueue(this.beforeHooks, next);
    }

    runQueue(queue, fn) {
        const step = index => {
            if (index >= queue.length) return fn();
            queue[index](() => step(index + 1));
        }
        step(0);
    }
}

五、站在巨人肩膀上看世界 🌍

5.1 与History路由的对比

特性 Hash路由 History路由
URL美观度 有#号 干净清爽
兼容性 IE8+ IE10+
服务端支持 无需特殊配置 需要服务端配合
SEO友好度 较差 较好
实现难度 简单 较复杂

5.2 真实框架源码赏析(Vue Router节选)

javascript 复制代码
// Vue Router的hash模式实现
setupListeners() {
    const eventType = supportsPushState ? 'popstate' : 'hashchange'
    window.addEventListener(
        eventType,
        handleRoutingEvent
    )
}

六、写在最后:从面试题到源码大佬的蜕变 🦋

通过这次手写Hash路由的旅程,我们不仅搞懂了:

  • 路由的基本工作原理 🕹️
  • 事件监听的正确姿势 🎧
  • 动态路由的魔法实现 ✨
  • 路由守卫的拦截艺术 🛡️

更重要的是掌握了通过源码学习框架的终极奥义。下次面试官再让你手写路由时,你可以微微一笑:"要不咱们聊聊Vue Router的导航守卫实现?" 😎

相关推荐
Fantasywt2 小时前
THREEJS 片元着色器实现更自然的呼吸灯效果
前端·javascript·着色器
IT、木易3 小时前
大白话JavaScript实现一个函数,将字符串中的每个单词首字母大写。
开发语言·前端·javascript·ecmascript
Mr.NickJJ4 小时前
JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件
开发语言·javascript·react.js
张拭心5 小时前
2024 总结,我的停滞与觉醒
android·前端
念九_ysl5 小时前
深入解析Vue3单文件组件:原理、场景与实战
前端·javascript·vue.js
Jenna的海糖5 小时前
vue3如何配置环境和打包
前端·javascript·vue.js
uhakadotcom5 小时前
Apache CXF 中的拒绝服务漏洞 CVE-2025-23184 详解
后端·面试·github
uhakadotcom5 小时前
CVE-2025-25012:Kibana 原型污染漏洞解析与防护
后端·面试·github
uhakadotcom5 小时前
揭秘ESP32芯片的隐藏命令:潜在安全风险
后端·面试·github
uhakadotcom5 小时前
Apache Camel 漏洞 CVE-2025-27636 详解与修复
后端·面试·github