《揭秘 Hash 路由:源码实现的优雅之道》

136 阅读3分钟

《揭秘 Hash 路由:源码实现的优雅之道》

前言

在当今互联网应用的蓬勃发展中,路由技术成为了构建流畅用户体验的关键要素之一。Hash 路由作为一种常见且实用的路由方式,以其独特的特性在众多前端框架和应用中发挥着重要作用。 在接下来的篇章中,我们将深入探索 Hash 路由源码的实现,揭开其神秘的面纱,一同领略其中的精妙之处。

代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <nav id="nav">
        <ul>
            <li><a href="#/page1">page1</a></li>
            <li><a href="#/page2">page2</a></li>
            <li><a href="#/page3">page3</a></li>
        </ul>
    </nav>
    <!-- router-view -->
    <div id="container"></div>
    <script>
        // 接下来是 JavaScript 部分的代码
    </script>
</body>
</html>

在 <body> 标签内,我们有一个导航栏 <nav> ,其中包含一个无序列表 <ul> ,列表中的每个项目都是一个链接。这些链接的 href 属性使用了 hash 值(例如 #/page1 、#/page2 、#/page3 )来指定不同的页面路径。

然后是一个 id 为 container 的 <div> 元素,它将用于动态显示根据路由变化加载的内容。

接下来看 JavaScript 部分的代码:

// 定义了一个名为 HashRouter 的类
class HashRouter {
    constructor() {
        // 初始化一个空的对象 routes,用于存储路由和对应的回调函数
        this.routes = {};
        // 为 window 对象添加 'hashchange' 事件的监听器,当哈希值变化时调用 this.load.bind(this) 函数
        window.addEventListener("hashchange", this.load.bind(this), false);
    }

    register(hash, callback) {
        // 用于注册路由和对应的回调函数
        this.routes[hash] = callback;
    }

    load() {
        // 打印当前页面的哈希值
        console.log(location.hash);
        // 去除哈希值开头的 '#' 符号,得到实际的路由部分
        let hash = location.hash.slice(1);
        console.log(hash, "///");
        // 初始化一个变量 hander,用于后续存储对应的回调函数
        let hander;
        // 如果哈希值为空,即没有指定具体路由,将 hander 赋值为 routes 对象中 'index' 对应的回调函数
        if (!hash) {
            hander = this.routes["index"];
        } else {
            // 否则,将 hander 赋值为 routes 对象中与当前哈希值对应的回调函数
            hander = this.routes[hash];
        }
        // 如果 hander 存在,使用 call 方法以当前对象为 this 上下文调用该回调函数
        hander && hander.call(this);
    }

    registerIndex(callback = function () {}) {
        // 用于注册首页的回调函数
        this.routes["index"] = callback;
    }
}

// 创建 HashRouter 类的实例 router
let router = new HashRouter();
// 获取页面中 id 为 'container' 的元素
let container = document.getElementById("container");
// 注册首页的回调函数,该函数将 '我是首页' 赋值给 container 的 innerHTML
router.registerIndex(() => (container.innerHTML = "我是首页"));
// 注册 '/page1' 路由的回调函数,该函数将 '我是 page1' 赋值给 container 的 innerHTML
router.register("/page1", () => (container.innerHTML = "我是page1"));
// 注册 '/page2' 路由的回调函数,该函数将 '我是 page2' 赋值给 container 的 innerHTML,并打印 this 和 routes 对象
router.register("/page2", function () {
    container.innerHTML = "我是page2";
    console.log(this, this.routes);
});
// 注册 '/page3' 路由的回调函数,该函数将 '我是 page3' 赋值给 container 的 innerHTML
router.register("/page3", () => (container.innerHTML = "我是page3"));

// 打印 router 对象的 routes 属性
console.log(router.routes);
// 调用 load 方法进行初始加载
router.load();

创建类的实例,调用类中的register和registerIndex存储方法,有哈希值的调用register,没有hash调用registerIndex作为首页,当监听时间发现hash值有变化那就执行this.load.bind(this),指定函数内部的 this 值为本实例对象,并且执行load方法,实现跳转并且执行了注册方法。