前端路由扫盲篇:Hash 模式和 History 模式到底怎么选?

如果把单页应用(SPA)比作一座功能齐备的大别墅,那么前端路由就是这座别墅里的"导览员"。当你在地址栏输入 URL 时,导览员就会根据地址,精准地把对应的页面组件搬到你的眼前。在这个分发过程中,主要有两位"导览员"可供选择:老成持重的 Hash 模式和现代时髦的 History 模式。很多初学者对它们的概念一知半解,面试时被问到区别也往往卡壳。今天,我们就来一场前端路由的扫盲,看看这两大模式各自的底牌是什么,在实际开发中又该如何做出抉择。

什么是路由

路由,本质上就是描述服务器上资源路径的一串地址。当你在浏览器里敲下一个 URL,后端会根据这个路径找到对应的资源,然后返回给你。

但到了前端,事情变得有点不一样。

前端路由:让 URL 和组件建立映射

现在的主流架构是 SPA(单页应用) ------整个应用只有一个 HTML 页面,切换"页面"时不再向服务端发起整页请求,而是由 JavaScript 动态替换当前页面的内容。

这时候问题就来了:页面没刷新,但用户看到的却是不同的"页面",那 URL 怎么办?

前端路由要解决的核心矛盾就是:

页面不刷新,但 URL 要变,而且浏览器前进后退要正常工作。

所以前端路由的本质,就是构建浏览器 URL 地址和组件之间的映射关系------URL 变了,渲染对应的组件,页面本身不刷新。

怎么实现"改 URL 但不刷新页面"

浏览器给了我们两条路。

一、Hash 模式

在浏览器眼里,URL 中 # 后面的部分叫做 hash (哈希值 / 片段标识符)。关键点在于:hash 的变更不会触发页面刷新,这是浏览器天生的行为。

例子:http://127.0.0.1:5500/hash.html#/home

这个链接中的 #/home 就是hsah值。

知道了这些内容那整个流程就清晰了:

  1. 监听 hashchange 事件
  2. 当url变更,hashchange 事件就会触发,通过location.hash获取当前hash值
  3. 拿着这个 hash 值去事先定义好的映射表里查找对应的组件,渲染出来。
javascript 复制代码
// 定义路由映射表
const routes = [
            {
                path: '/home',
                component: () => {
                    return '<h2>首页页面</h2>'
                }
            },
            {
                path: '/hot',
                component: () => {
                    return '<h3>hot page</h3>'
                }
            }
        ]
        
// 监听 hash 变化
window.addEventListener('hashchange', () => {
// 在routes中找到对应的path,并渲染对应的组件
    rederView(location.hash)
})
function rederView (hashVal){
    for(let i =0;i<routes.length;i++){
        if('#'+routes[i].path == hashVal){
            document.getElementById('root').innerHTML = routes[i].component()
           }
      }
}
        

优点:兼容性好,实现简单,不需要服务端配合。

缺点:URL 里永远带着一个 #,不够优雅。

二、History 模式

HTML5 给 window.history 对象新增了几个 API,让前端有能力直接操控浏览器历史记录,而不触发页面刷新:

scss 复制代码
pushState() 向浏览器的历史记录栈中添加一条记录
popState() 从浏览器的历史记录栈中出栈一条记录
replaceState() 替换浏览器的历史记录栈中的栈顶记录

pushStatereplaceState 都能直接修改浏览器地址栏的 URL,并且不会导致页面刷新------这正是我们需要的。

那前进后退怎么办?浏览器在用户点击前进或后退按钮时会触发 popstate 事件,我们只需要监听它,就能感知用户的历史导航行为,然后渲染对应组件。

javascript 复制代码
        const routes = [
            {
                path: '/home',
                component: () => {
                    return '<h2>history 首页页面</h2>'
                }
            },
            {
                path: '/hot',
                component: () => {
                    return '<h3>history hot page</h3>'
                }
            }
        ]

        window.addEventListener('DOMContentLoaded', () => {
            // 默认展示
            onLoad()
        })

        window.addEventListener('popstate',()=>{   // 监听浏览器的前进后退事件
            renderView(location.pathname)
        })

        function onLoad() {  // 路径变更渲染对应组件
            let linkList = document.querySelectorAll('a[href]')
            // console.log(linkList);
            linkList.forEach(el => {
                el.addEventListener('click', (e) => {
                    e.preventDefault()   // 阻止默认行为

                    history.pushState(null, '', el.getAttribute('href'))  // 进入浏览器的缓存栈

                    renderView(location.pathname)
                })
            })

        }

        function renderView(pathname) {
            routes.forEach((item) => {
                if (pathname == item.path) {
                    document.getElementById('root').innerHTML = item.component()
                }
            })
        }

优点:URL 干净,没有 #,和传统多页应用的 URL 完全一致。

缺点:需要服务端配合------把所有路径都 fallback 到 index.html,否则用户直接访问或刷新深层路径会 404

总结

Hash 模式简单粗暴,靠的是浏览器的"历史遗留特性";History 模式优雅现代,靠的是 HTML5 新增的 API。理解了这两套机制,你就掌握了前端路由的底层原理------不管是 Vue Router 还是 React Router,本质上都是在这两个模式之上做了一层封装和增强。

相关推荐
用户059540174461 小时前
LLM对话记忆测试踩坑实录:手工回归30分钟,自动化后2分钟发现3个隐藏Bug
前端·css
Ashley的成长之路2 小时前
前端性能优化实战手册·第2篇:资源加载策略全解
前端·性能优化·资源加载·http/3·性能优化实战·资源加载优化
浅水壁虎2 小时前
vue基础(第二章 )
前端·javascript·vue.js
界面开发小八哥2 小时前
界面控件DevExtreme v26.1新版亮点——支持Angular 22
前端·javascript·angular.js·devexpress·ui开发·devextreme
Getflare2 小时前
前端 + UI 设计 + AI:这不是三个工种,是一个新三角能力模型(附自检清单)
前端·人工智能·ui
oil欧哟3 小时前
我做了一个 Vibe Coding 术语学习站:VibeHub
前端·ai·agent·独立开发·vibe coding
审小匠OpenCPAi3 小时前
银行流水核查怎么自动化?单边匹配、双向勾稽与图聚类异常检测的工程对比
java·前端·人工智能·python·审计
Revolution613 小时前
一个公共表格组件,是怎么一步步失控的
前端·前端工程化
腻害兔4 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:CRM 客户关系模块深度解析——从线索到回款,一套完整的 B2B 销售闭环是怎么搭的?
java·前端·javascript·vue.js·产品经理·ai编程