基于react的hash路由简易实现
背景介绍
在SPA项目中,如果展示页面繁多,根据前端工程化理论中组件化思想,我们通常会将不同的页面,按照功能,职责等进行划分。 这个时候,每个页面,就需要一个相应的路由去对应。
现在react社区中已经有react-router这个成熟的轮子,我们可以直接引入并使用。
但具体hash路由是如何实现的,我们现在就来探讨一下...
在路由设计中,我们用到了设计模式中的 单例模式 观察者模式 中介者模式
所以如果有该设计模式基础或实践的小伙伴,会更容易看懂一些...
开始
首先我们要了解一下,监听路由改变,其实是监听2个事件并操作回调,进行渲染:
- 页面加载事件(window.onload)
- 页面url中hash部分改变事件(window.onhashchange)
原生html代码编写
<!doctype html>
<html>
<head>
<title>hash-router</title>
</head>
<body>
<ul>
<li><a href="#/">home</a></li>
<li><a href="#/login">
login
<ul>
<li><a href="#/login/login1">login1</a></li>
<li><a href="#/login/login2">login2</a></li>
<li><a href="#/login/login3">login3</a></li>
</ul>
</a></li>
<li><a href="#/abort">abort</a></li>
</ul>
<div id = "content"></div>
</body>
</html>
复制代码
在这里 我们指定了一系列路由,包括"#/","#/login"等。然后,我们就要针对页面onload事件和点击每个a标签之后hash改变事件来进行处理了。
原生js代码编写
创建一个HashRouter类
<script type = 'text/javascript'>
"use strict"
class HashRouter{
constructor(){
this.routers = {},
this.init();
}
}
</script>
复制代码
- 在这段代码中 我们创建了一个HashRouter类,并且指定了一个routers空对象来存储路由信息。
- 将来在这个routers对象中,我们存储路由的名称及回调,如routers = { "#/login" : [ callback1, callback2 ] }。键名为hash路由名称,键值为数组,数组内为跳转到该路由所要执行的回调方法。
- 现在,我们来执行init方法,当HashRouter类被创建的时候,我们需要执行的一些初始化方法
当HashRouter类被创建时,执行的初始化方法
<script type = 'text/javascript'>
"use strict"
class HashRouter{
constructor(){
this.routers = {},
this.init();
}
trigger(){
//取出当前url中的hash部分,并过滤掉参数
let hash = window.location.hash && window.location.hash.split('?')[0];
//在routers中,找到相应的hash,并执行已保存在其中的回调方法
if(this.routers[hash] && this.routers[hash].length > 0){
for(let i = 0 ; i < this.routers[hash].length ; i++){
this.routers[hash][i]();
}
}
}
init(){
window.addEventListener('load', () => this.trigger(), false);
window.addEventListener('hashchange', () => this.trigger(), false);
}
}
</script>
复制代码
兼听了页面加载时和hash改变时事件,并针对改变事件做回调处理,即trigger方法
需要抛出api,让开发者添加监听事件回调
在上一步我们进行了初始化,监听了页面hash改变的事件并做相应的处理。但是,我们需要执行的回调方法,需要开发人员手动添加才行。
<script type = 'text/javascript'>
"use strict"
class HashRouter{
constructor(){
this.routers = {},
this.init();
}
listen(path, callback){
//如果routers中已经存在该hash,则为它pushcallback方法,否则新建一个相应数组,并push回调方法
if(!this.routers[path]){
this.routers[path] = [];
}
this.routers[path].push(callback);
}
trigger(){
//取出当前url中的hash部分,并过滤掉参数
let hash = window.location.hash && window.location.hash.split('?')[0];
//在routers中,找到相应的hash,并执行已保存在其中的回调方法
if(this.routers[hash] && this.routers[hash].length > 0){
for(let i = 0 ; i < this.routers[hash].length ; i++){
this.routers[hash][i]();
}
}
}
init(){
window.addEventListener('load', () => this.trigger(), false);
window.addEventListener('hashchange', () => this.trigger(), false);
}
}
<