原生JavaScript路由实现指南:History API与popstate事件详解
本文详解如何使用原生JavaScript实现前端路由,无需依赖React等框架。核心在于利用History API的pushState方法修改URL而不刷新页面,并通过监听popstate事件响应浏览器前进后退操作。文章涵盖路由配置、状态管理及DOM更新逻辑,帮助开发者构建轻量级、低依赖的单页应用导航系统。
前言
提及路由时,人们往往联想到React等框架。然而,这些框架底层依然依赖原生JavaScript(vanilla JavaScript)。本文将探讨如何基于原生JS实现路由功能。
本教程旨在指导读者掌握使用原生JavaScript编写自定义路由的方法。
简介
许多开发者出于特定需求希望自建路由系统。如果你阅读此文,很可能正面临此类需求。
使用原生JS构建路由的核心优势在于降低对重型框架的依赖。
只要掌握实现路由所需的各个组成部分,在原生JavaScript中构建路由系统并非难事。
开发JS路由时需掌握以下关键点:
1. 原生JS路由的核心机制依赖于location.pathname属性。
2. 需监听“popstate”事件以响应pathname的变化。当用户在地址栏输入新URL或点击浏览器导航按钮而不希望刷新页面时,该事件触发,此时需通过加载新内容来更新视图。
3. 可选择将路由规则存储在routes[]数组中。
4. 必须使用JavaScript正则表达式(RegEx)来解析URL。
5. 若要将路由集成到浏览器原生架构中,理解history对象及history.pushState(JavaScript History API)至关重要。
首先,我们将处理History API。
JavaScript的History API
许多Vanilla JS路由教程忽略了JavaScript History API,这并不恰当。因为浏览器的“后退”和“前进”按钮与浏览历史记录中的URL导航直接相关。脱离History API,路由无从谈起。
1. history.back()等同于history.go(-1),对应于用户点击浏览器“后退”按钮时的行为。两种方法效果一致。
2. 当用户点击“前进”按钮时,执行history.forward(),这等同于history.go(1)。
3. go()方法与.back()和forward()类似,但允许指定在浏览器历史记录栈中前进或后退的具体步数。
4. pushState()用于将新状态推送到History API中。
5. .length属性表示会话历史记录中的元素数量。
6. .state属性用于获取当前状态,无需监听“popstate”事件即可访问。
实现自己的原生JS路由
基于History API的Vanilla JS路由设置
我们先深入研究构建URL切换器所需的最少代码(无需刷新页面),随后通过GIF动图展示其工作原理。
<html>
<head>
<title>Hello</title>
<script type = "module">
function select_tab(id) {
// remove selected class from all buttons
document.querySelectorAll(".route").forEach(item => item.classList.remove('selected'));
// select clicked element (visually)
document.querySelectorAll("#" + id).forEach(item => item.classList.add('selected'));
}
function load_content(id) {
// Update text "Content loading for {id}..."
// Of course, here you would do you content loading magic
// Perhaps run Fetch API to update resources
document.querySelector("#content").innerHTML = 'Content loading for /' + id + '...';
}
function push(event) {
// Get id attribute of the box or button or link clicked
let id = event.target.id;
// Visually select the clicked button/tab/box
select_tab(id);
// Update Title in Window's Tab
document.title = id;
// Load content for this tab/page
loadContent(id);
// Finally push state change to the address bar
window.history.pushState({id}, `${id}`, `/page/${id}`);
}
window.onload = event => {
// Add history push() event when boxes are clicked
window["home"].addEventListener("click", event => push(event))
window["about"].addEventListener("click", event => push(event))
window["gallery"].addEventListener("click", event => push(event))
window["contact"].addEventListener("click", event => push(event))
window["help"].addEventListener("click", event => push(event))
}
// Listen for PopStateEvent (Back or Forward buttons are clicked)
window.addEventListener("popstate", event => {
// Grab the history state id
let stateId = event.state.id;
// Show clicked id in console (just for fun)
console.log("stateId = ", stateId);
// Visually select the clicked button/tab/box
select_tab(stateId);
// Load content for this tab/page
loadContent(id);
});
</script>
<style>
* { /* global font */
font-family: Verdana;
font-size: 18px;
}
#root { display: flex; flex-direction: row; }
#content { display: flex;
display: block;
width: 800px;
height: 250px;
/* vertically centered text */
line-height: 250px;
border: 2px solid #555;
margin: 32px;
text-align: center;
}
.route {
cursor: pointer;
justify-content: center;
width: 150px;
height: 50px;
/* vertically centered text */
line-height: 50px;
position: relative;
border: 2px solid #555;
background: white;
text-align: center;
margin: 16px;
}
.route.selected { background: yellow; }
</style>
</head>
<body>
<section id = "root">
<section class = "route" id = "home">/home</section>
<section class = "route" id = "about">/about</section>
<section class = "route" id = "gallery">/gallery</section>
<section class = "route" id = "contact">/contact</section>
<section class = "route" id = "help">/help</section>
</section>
<main id = "content">Content loading...</main>
</body>
</html>
核心在于调用window.history.pushState({id}, ${id}, /page/${id});
第一个参数是状态的唯一ID,第二个参数是“标签标题”文本,第三个参数是地址栏中显示的路径。这使得浏览器无需重新加载页面即可更改URL。
结果:每次点击按钮时,浏览器地址栏中的URL实际上会发生变化,同时内容框也会更新。
我们的原生JS路由开始运行。请注意,每次点击按钮都会触发history.pushState。我们只需将存储在元素id属性中的点击元素ID(如home、about、gallery等)传递给它即可。这些ID应与实际导航到的页面一致。当然,存储页面名称并非只有这一种方式,也可以使用数组[]或其他方法。本例采用此方式。
当然,我们还需要从服务器加载该位置的布局和资源。具体实现取决于你的程序,可以是任何形式。
使“后退”和“前进”按钮起作用
使用history.pushState会自动使Back和Forward按钮导航到上一个或下一个状态。这将触发popstate事件。你需要在此处再次更新视图(第一次更新发生在点击按钮时)。
由于该事件包含点击的id,因此当用户点击Back或Forward时,刷新视图并重新加载内容变得容易。
这里未使用React或Vue,因此在我的源代码中,load_content负责直接在DOM中更新视图。此区域可以填充由API加载的内容。由于这仅是“前端”示例,无法展示更多后端细节。但这展示了客户端的工作方式。
初始化服务器端的路由负载
将它们结合起来还需要执行一个步骤。在我的例子中,只使用了router.html。当你在PWA中首次加载此路由时,必须确保如果直接在地址栏中输入/page/home,路由也能正常工作。
到目前为止,我们仅从前端更改了路由器地址。假设每次你导航到路由按钮上出现的URL时,该URL实际上是从服务器单独加载的。
因此,你有责任确保/page/about将路由器和页面加载到应用程序的根视图中。它还应高亮显示“current”按钮。
实施完毕后,你的路由即告完成。你如何选择重新加载#content元素中的内容完全取决于你自己和你的后端设计。
以上就是如何制作自己的原生JavaScript路由的详细内容,更多关于制作原生JavaScript路由的资料请关注本站其它相关文章!
JavaScript将图片转换为Base64编码的两种途径
2026-11-05 16:43:05
怎么在 Vue 或 React 项目中正确集成 animate.js
2026-11-05 10:23:38
JS注册事件:addEventListener基础语法与参数详解
2026-11-05 08:44:56
JSP和JS的区别:运行环境与服务端/客户端职责解析
2026-11-04 23:34:36
pdf.js 官方下载地址:GitHub Release 获取预编译包
2026-11-04 23:07:27
