本文通过40行原生JavaScript代码,深入解析前端路由的三种核心模式:HashRouter、HistoryRouter和MemoryRouter。文章详细阐述了基于hashchange事件、pushState API以及localStorage存储的不同实现原理,对比了各模式在URL表现、后端配置需求及适用场景上的差异,帮助开发者理解路由底层逻辑并掌握简易实现方法。
前言
在使用Vue或React等框架的路由功能时,由于对其底层实现机制理解不足,常导致在掌握其设计思想时遇到困难。为此,本文尝试使用原生JavaScript实现一个简易的前端路由,并梳理前端开发中常见的几种路由模式及其区别,旨在辅助读者更好地学习和理解路由机制。
路由到底是一个什么东西?
从网络通信的角度来看,路由(routing)是指通过互联的网络将信息从源地址传输到目的地址的过程。在OSI网络参考模型中,这一过程发生在第三层,即网络层。
通俗来说,前端路由的作用是将一个url发送到后端服务器,从而获取对应的后端资源或页面内容。
实现一个 hashRouter
// html部分
<body>
<a href="#1" rel="external nofollow" >go to 1</a>
<a href="#2" rel="external nofollow" >go to 2</a>
<a href="#3" rel="external nofollow" >go to 3</a>
<a href="#4" rel="external nofollow" >go to 4</a>
<div id="app"></div>
<div id="div404" style="display: none;">你要找的内容被狗吃了</div>
<script src="index.js"></script>
</body>
// js
const app = document.getElementById('app')
const div1 = document.createElement('div')
div1.innerHTML = '1.components'
const div2 = document.createElement('div')
div2.innerHTML = '2.components'
const div3 = document.createElement('div')
div3.innerHTML = '3.components'
const div4 = document.createElement('div')
div4.innerHTML = '4.components'
const routerMap = {
"1": div1,
"2": div2,
"3": div3,
"4": div4
}
function route(container) {
// 获取路由hash 值
let hashName = window.location.hash.substr(1)
console.log(hashName)
hashName = hashName || '1'
// 获取路由对应的组件
const components = routerMap[hashName]
if(!components) {
// 404
components = document.querySelector("#div404");
}
components.style.display = 'block'
console.log(components)
// 展示界面
container.innerHTML = "";
container.appendChild(components);
}
route(app)
window.addEventListener('hashchange', () => {
route(app);
})至此,一个基于JavaScript的路由基本功能已完成(暂不考虑嵌套场景)。其核心思路是利用HTML5提供的change API来监听URL中哈希值(hash)的变化,并根据变化渲染不同的组件或页面内容。
historyRouter
const app = document.getElementById('app')
const div1 = document.createElement('div')
div1.innerHTML = '1.components'
const div2 = document.createElement('div')
div2.innerHTML = '2.components'
const div3 = document.createElement('div')
div3.innerHTML = '3.components'
const div4 = document.createElement('div')
div4.innerHTML = '4.components'
const routerMap = {
"/1": div1,
"/2": div2,
"/3": div3,
"/4": div4
};
function route(container) {
// 获取路由hash 值
let hashName = window.location.pathname;
if (hashName === "/") {
hashName = "/1";
}
// 获取路由对应的组件
let components = routerMap[hashName]
if(!components) {
// 404
components = document.querySelector("#div404");
}
components.style.display = 'block'
// 展示界面
container.innerHTML = "";
container.appendChild(components);
}
const allA = document.querySelectorAll("a.link");
for(let a of allA) {
a.addEventListener('click', ()=> {
e.preventDefault();
const href = a.getAttribute("href");
window.history.pushState(null, `page ${href}`, href);
// 路由变化更新
onStateChange(href);
})
}
route(app)
function onStateChange() {
route(app);
}在HTML结构方面,History模式与Hash模式保持一致。主要区别在于,History模式无法直接监听路由变化,因此需要在路由变更时手动更新组件状态。此外,实现该模式所使用的API变为了window.history.pushState。
memoryRouter
MemoryRouter在前端开发中接触较少,因为它主要应用于App环境,不太适合H5页面或PC端浏览器。这种路由模式适合那些用户不可见、无需真实URL交互的场景。
const app = document.getElementById('app')
const div1 = document.createElement('div')
div1.innerHTML = '1.components'
const div2 = document.createElement('div')
div2.innerHTML = '2.components'
const div3 = document.createElement('div')
div3.innerHTML = '3.components'
const div4 = document.createElement('div')
div4.innerHTML = '4.components'
const routerMap = {
"/1": div1,
"/2": div2,
"/3": div3,
"/4": div4
};
function route(container) {
// 获取路由hash 值
let number = window.localStorage.getItem("pathName");
if (hashName === "/") {
hashName = "/1";
}
// 获取路由对应的组件
let components = routerMap[hashName]
if(!components) {
// 404
components = document.querySelector("#div404");
}
components.style.display = 'block'
// 展示界面
container.innerHTML = "";
container.appendChild(components);
}
const allA = document.querySelectorAll("a.link");
for(let a of allA) {
a.addEventListener('click', ()=> {
e.preventDefault();
const href = a.getAttribute("href");
window.localStorage.setItem("pathName", href);
// 路由变化更新
onStateChange(href);
})
}
route(app)
function onStateChange() {
route(app);
}上述代码已实现了MemoryRouter,其本质是将路由信息隐藏起来,并在需要时从存储中获取当前路径。
最后做个总结
hashRouter 典型的就是路由中会出现#, 在页面中会出现锚点跳转, 还有一个重要需要注意的点就是, 使用 hashRouter 在 # 后面拼接的东西会被浏览器屏蔽掉, 谷歌推出了一个方法, 就是在前面加上一个!但是也不能根治- HistoryRouter的URL中不包含#号,外观更加简洁美观。然而,这需要后端配合前端进行配置,因为当路由发生变化且用户刷新页面时,后端必须将所有URL请求重定向到同一个页面(404页面除外)。
- MemoryRouter使用频率较低,了解其原理即可。它相当于一种“不可见”的路由机制,主要出现在App开发中。
最后附上github链接
到此这篇关于40行原生js代码实现前端简易路由的文章就介绍到这了,更多相关原生js实现前端路由内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!