从hash技术原理出发,探讨前端路由的实现过程

203 阅读7分钟

前言

在现代 web 开发中,单页面应用(SPA)已经成为了一种不可或缺的模式,而前端路由则是其背后的核心驱动力。想象一下,如果没有前端路由,用户在浏览你的网站时,每次点击链接都必须重新加载整个页面,那将会是怎样一种体验?页面闪烁、等待加载、丧失了流畅感——用户的耐心瞬间耗尽,留不住访客的可能性骤然增加。

前端路由通过在不刷新页面的情况下根据 URL 的变化动态加载内容,让用户能够无缝地探索应用的不同部分。这不仅提升了用户体验,还大幅度提高了应用的性能和响应速度。它就像是一个无形的桥梁,让用户在信息的海洋中尽情游历。

前端路由主要依靠两种技术:hash 和 history。这两者使得我们能够在改变 URL 的同时,确保页面的状态保持不变、体验依然流畅。尤其是在移动设备普及的今天,加载速度和用户体验变得尤为重要。

综上所述,前端路由不仅仅是一个技术细节,而是现代 web 开发中提升用户体验、增强应用性能的重要工具。对于想要在激烈竞争中脱颖而出的开发者来说,熟练掌握前端路由的运作原理,将为你的项目成功奠定坚实的基础。

而今天我来从hash技术原理出发,探讨前端路由的实现过程。

正文

1. 路由基础

在讲之前,我们先了解一点路由基础:

在传统的多页面应用(MPA)中,每当用户点击一个链接时,浏览器会重新加载页面,并根据服务器返回的内容渲染新的视图。而在 SPA 中,前端路由则承担了不同视图的切换任务。前端路由的核心就是根据 URL 地址的变化,动态地加载相应的组件,而不刷新整个页面。简单来说,我们想实现前端路由只需要解决以下两个问题:

  • 如何修改 URL,而不引起页面刷新
  • 如何检测 URL 变化

2. Hash 路由

然后我们再来简单了解一下Hash路由。

Hash 路由是最早实现前端路由的方式,它通过修改 URL 中的 # 符号后的部分来表示不同的路由。# 后的内容并不会被发送到服务器,因此修改它不会导致页面的重新加载,浏览器也不会重新发送 HTTP 请求。

也就是在浏览器url后拼接 #xxxx 会被认为是hash值,而hash值的变更是不会引起浏览器页面的刷新。

image.png

正如这图中一样,url地址后面加#aaaaaaaaaaa并不会引起浏览器页面的刷新。

代码部分

话不多说,我们直接代码展示:

<!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>
    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>

    <div id="routeView">
        <!-- 放一个代码片段 -->

    </div>

    <script>
        const routes = [
            {
                path: '#/home',
                component: '首页页面内容',
            },
            {
                path: '#/about',
                component: 'about page'
            }
        ]


        const routeView = document.getElementById('routeView')
        window.addEventListener('DOMContentLoaded',onHashChange)
        window.addEventListener('hashchange', onHashChange)

        function onHashChange() {
            console.log(location.hash);
            routes.forEach((item, index) => {
                if(item.path === location.hash) {
                    routeView.innerHTML = item.component
                }
            })
        }


    </script>
</body>

</html>

代码讲解

我这段代码展示了一个简单的基于 Hash 路由 的前端路由实现。它通过监听 URL 中 hash 值的变化,动态切换不同的页面内容,而无需重新加载整个页面。接下来我会给各位详细讲解一下。


核心原理

  1. URL 中的 Hash (#) 使用

    • 在浏览器 URL 中,# 后面的部分被称为 hash 值,不会被发送到服务器,而仅由浏览器解析。
    • 修改 hash 值不会引起页面的刷新,因此它成为前端路由实现的一种简便方式。
  2. 事件监听

    • hashchange 事件:就是当 URL 中的 hash 部分发生变化时,也就是#后面的值变化,会触发 hashchange 事件。代码中的 window.addEventListener('hashchange', onHashChange) 监听了这一事件。
    • DOMContentLoaded 事件:就是当初始页面加载完成后,触发 DOMContentLoaded 事件,确保页面加载时也会执行一次 onHashChange(),显示正确的初始内容。
  3. 路由匹配逻辑

    • onHashChange 函数中,通过遍历定义的 routes 数组,检查当前 location.hash 是否与某个路由的 path 匹配。
    • 如果匹配成功,将对应的 component 内容插入到 routeView 容器中,实现页面的动态内容切换。

详细代码解析

  1. HTML 结构

    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>
    
    <div id="routeView"></div>
    
    • 导航链接:点击 <a> 标签时,URL 的 hash 值会改变,比如点击“首页”会将 URL 修改为 http://baidu.com/#/home
    • routeView 容器:用于展示当前路由对应的组件内容。
  2. JavaScript 路由逻辑

    const routes = [
        { path: '#/home', component: '首页页面内容' },
        { path: '#/about', component: 'about page' }
    ];
    
    const routeView = document.getElementById('routeView');
    window.addEventListener('DOMContentLoaded', onHashChange);
    window.addEventListener('hashchange', onHashChange);
    
    function onHashChange() {
        console.log(location.hash);
        routes.forEach((item) => {
            if (item.path === location.hash) {
                routeView.innerHTML = item.component;
            }
        });
    }
    
    • routes 数组:定义了路由规则,每个路由对象包含:

      • path:URL 中的 hash 路径(如 #/home)。
      • component:与该路径匹配时要显示的内容。
    • 事件监听设置

      • DOMContentLoaded 监听器:确保页面加载完成时执行一次路由匹配,显示正确的初始内容。
      • hashchange 监听器:当用户通过点击链接或使用浏览器的前进/后退按钮改变 hash 时,会触发该事件。
    • onHashChange 函数

      • 获取当前的 hash 值并与 routes 中定义的路径进行比对。
      • 如果找到匹配的路径,将对应的 component 内容渲染到 routeView 容器中。

而我们前文说过,实现前端路由原理需要解决的两个问题是: 1.如何修改 URL,而不引起页面刷新, 2.如何检测 URL 变化。现在看来是不是都解决了,在url后面加#解决第一个问题,hashchange事件解决第二个问题,到这就基本解决了前端路由原理问题,当然我这里只是讲最基本的东西,前端路由原理肯定不止这些,这就不多讲。我再解释一下代码里的location.hash,它 是 JavaScript 中 Location 对象的一个属性,它返回当前 URL 中井号(#)之后的部分。routeView.innerHTML = item.component; 这一行代码的作用是将 DOM 元素 routeView 的 HTML 内容更新为 item.component 的值。以防有小伙伴不懂。

前端路由原理体现

  1. 无页面刷新机制

    • 修改 hash 值不会导致页面重新加载,这样就可以在同一个 HTML 文档中实现不同视图之间的切换。
    • hashchange 事件确保了内容的更新仅限于 routeView 容器内,而不是重新渲染整个页面。
  2. 路由映射机制

    • 通过维护一个 routes 数组,将 URL 路径与页面内容或组件进行映射。
    • URL 改变触发 onHashChange,实现从 URL 到组件的动态映射。
  3. 用户体验优化

    • URL 的变化反映了用户当前所在的页面状态,用户可以使用浏览器的前进和后退按钮在不同视图间切换,而不会丢失当前状态。

这段代码充分体现了基于 Hash 路由 的核心机制:通过监听 hash 值的变化,动态渲染不同的内容,从而实现前端页面的无刷新切换。这种方法简单易用,非常适合构建小型单页面应用。

小结

前端路由的核心在于修改 URL 而不刷新页面,从而实现单页面应用的流畅体验。hash 路由简单易用,但在 URL 设计上有一定的局限;而 history 路由则提供了更加灵活和优雅的解决方案,适合复杂的 SPA 项目。而这里我们只讲了hash路由,在下篇文章我会给大家讲讲history路由,觉得有帮助的伙伴点点赞和关注。