前言
在写前端代码初期,跳转页面都是用a标签加链接实现点击跳转,但是遇到无需重新加载整个页面,只需更新部分内容或者动态内容加载的时候,直接跳转新页面的操作过于费时和不便,因此,接下来我们学习一种更便捷好用的技术——HashRouter。
HashRouter的学习
什么是HashRouter?
HashRouter 是一种在单页应用(SPA)中实现前端路由的技术,它利用了URL中的哈希部分(#)来模拟不同的页面状态,而无需实际请求新的HTML文档。这种技术对于早期的SPA非常有用,因为它绕过了服务器端的路由逻辑,允许客户端直接处理URL变化。
以下是 HashRouter 的工作原理和关键点:
HashRouter 工作原理
- URL中的哈希部分:在URL中,
#
符号后面的部分被称为哈希部分,例如,在URLhttps://example.com/#/page2
中,#/page2
就是哈希部分。 - hashchange 事件:每当URL的哈希部分发生变化时,浏览器会触发
hashchange
事件。前端代码可以监听这个事件,然后根据哈希值的变化来更新页面的内容。 - 客户端路由逻辑:在
hashchange
事件处理器中,可以编写逻辑来解析哈希值,并根据解析结果决定展示哪个组件或视图。这通常涉及到查找一个路由表或路由映射,将哈希值映射到相应的视图组件上。 - 更新视图:根据解析后的哈希值,使用 Vue 的
<router-view>
组件或其他机制来更新显示的视图,而无需重新加载整个页面。
优缺点
优点:
- 无需服务器端路由支持:HashRouter 不需要服务器端的配合,所有的路由逻辑都在客户端实现,这使得部署和维护变得更加简单。
- 兼容性好:几乎所有的浏览器都支持
hashchange
事件,因此 HashRouter 在各种浏览器上的表现更加一致。
缺点:
- SEO(搜索引擎优化)问题:由于URL的哈希部分不会被包含在服务器返回的原始HTML中,这可能会导致搜索引擎无法正确索引SPA中的各个“页面”。
- 用户体验:虽然SPA提供了流畅的用户体验,但某些用户可能不习惯没有完整页面加载的感觉,特别是在复杂的操作或大型应用中。
手写HashRouter
HTML结构
- 页面包含一个导航栏(
<nav>
),其中包含三个链接到不同哈希值的锚点(<a>
标签)。 - 页面主体部分(
<div id="container">
)用于展示根据当前哈希值动态加载的内容。
JavaScript逻辑
HashRouter
类
- 构造函数:初始化一个空的路由表(
routes
),并监听hashchange
事件,以便在哈希值发生变化时调用load
方法。 register
方法:接受一个哈希值和一个回调函数,将它们添加到路由表中。registerIndex
方法:为默认或空哈希值设置回调函数。load
方法:读取当前的哈希值,从路由表中查找对应的回调函数,并执行它。如果没有找到匹配项,则执行首页的回调函数。
初始化和注册路由
- 创建
HashRouter
实例router
。 - 获取页面容器元素
container
。 - 注册首页、
/page1
、/page2
和/page3
的路由回调,每个回调都会更新container
的innerHTML
属性,显示对应页面的内容。 - 打印路由表
router.routes
。 - 初始加载页面内容。
功能描述
当用户点击导航链接或直接修改URL的哈希值时,浏览器会触发hashchange
事件。HashRouter
监听此事件并通过load
方法检查当前哈希值,从而决定加载哪个页面的内容到container
元素中。这种方式允许用户在不完全刷新页面的情况下浏览不同的虚拟“页面”。
源代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手写Hash Router</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">
<script>
/**
* HashRouter类用于实现基于URL哈希值的路由管理。
* 它监听hashchange事件,根据URL的哈希部分加载对应的处理函数。
*/
class HashRouter {
/**
* 构造函数初始化路由表,并设置hashchange事件监听器。
*/
constructor() {
this.routes = {}; // page => Component
window.addEventListener('hashchange',
this.load.bind(this), false)
}
/**
* 注册一个路由处理函数。
* @param {string} hash - URL的哈希值部分。
* @param {Function} callback - 当路由匹配到指定hash时执行的函数。
*/
register(hash, callback = function() {}) {
this.routes[hash] = callback;
}
/**
* 注册默认的路由处理函数,用于处理空哈希值。
* @param {Function} callback - 当路由为空时执行的函数。
*/
registerIndex(callback = function() {}) {
this.routes['index'] = callback
}
/**
* 根据当前URL的哈希值加载对应的路由处理函数。
*/
load() {
// console.log(location.hash); // BOM
let hash = location.hash.slice(1) // 去掉# 方是路由
console.log(hash, '////');
let handler;
if (!hash) {
// 首页
handler = this.routes['index']
} else {
// 相应页面
handler = this.routes[hash]
}
handler && handler.call(this)
}
}
let router = new HashRouter();
let container = document.getElementById('container');
// 注册首页路由处理函数
router.registerIndex(() => container.innerHTML = '我是首页')
// 注册page1路由处理函数
router.register('/page1', () => container.innerHTML = '我是Page1')
// 注册page2路由处理函数
router.register('/page2', function() {
// console.log(this, this.routes)
container.innerHTML = '我是Page2'
})
// 注册page3路由处理函数
router.register('/page3', () => container.innerHTML = '我是Page3')
console.log(router.routes);
// 初始加载路由
router.load()
</script>
</div>
</body>
</html>