通过手写hash路由理解单页应用

  在现代前端开发中,前端路由几乎是单页应用(SPA)不可或缺的一部分。Vue Router、React Router 等流行工具对路由进行了很好的实现,本文将通过一个简单的 Hash 路由实现一个简单的前端路由系统,帮助更好的理解路由实现逻辑。

前端路由

前端路由的核心目标就是:在不刷新页面的前提下,根据 URL 的变化加载不同的页面内容

常见的路由模式有两种:

  • Hash 模式:基于 URL 的 # 号之后的内容,例如 /#/about
  • History 模式:使用 HTML5 提供的 history.pushState() API,URL 更“干净”,例如 /about

本文我聚焦于实现成本最低的 Hash 模式 路由。

手写一个 Hash 路由

下面是完整的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>手写前端路由</title>
</head>
<body>
<nav>
<a href="#/">首页</a>
<a href="#/about">关于</a>
<a href="#/contact">联系</a>
</nav>

<div id="app"></div>

<script>
class Router {
constructor(routes) {
this.routes = routes // 路由表
this.app = document.getElementById("app") // 渲染目标
window.addEventListener("hashchange", this.routeChange.bind(this))
window.addEventListener("load", this.routeChange.bind(this)) // 初始加载
}

routeChange() {
const hash = location.hash.slice(1) || "/" // 获取 hash,默认为 /
const component = this.routes[hash]
if (component) {
this.app.innerHTML = component()
} else {
this.app.innerHTML = "<h2>404 Not Found</h2>"
}
}
}

// 定义组件
const Home = () => "<h2>欢迎来到首页</h2>"
const About = () => "<h2>关于我们</h2>"
const Contact = () => "<h2>联系我们</h2>"

// 初始化路由
const routes = {
"/": Home,
"/about": About,
"/contact": Contact,
}

const router = new Router(routes)
</script>
</body>
</html>

路由表

通过一个对象来维护路由与组件之间的映射关系:

1
2
3
4
5
const routes = {
"/": Home,
"/about": About,
"/contact": Contact,
}

每个路径对应一个函数(组件),返回一段 HTML 字符串。

监听 URL 变化

使用原生事件监听器:

1
2
window.addEventListener("hashchange", this.routeChange.bind(this))
window.addEventListener("load", this.routeChange.bind(this))
  • hashchange:当地址栏的 # 部分变化时触发;
  • load:页面首次加载时触发一次。

解析并渲染页面内容

1
2
3
const hash = location.hash.slice(1) || "/" 
const component = this.routes[hash]
this.app.innerHTML = component ? component() : "<h2>404 Not Found</h2>"

通过 location.hash 获取当前的 hash 值,去除 # 后再匹配路由表。

最终效果

点击顶部导航栏的链接时,页面内容会即时更新,无需刷新页面:

  • #/ → 首页
  • #/about → 关于页面
  • #/contact → 联系页面

小结

前端路由的核心其实非常纯粹:监听 URL 变化 → 匹配路由表 → 渲染对应内容

当然,实际项目中的路由功能远不止于此,比如嵌套路由、动态参数、路由守卫等,但这个手写例子是一个很好的理解路由的切入点。

作者

Fu9Zhou

发布于

2025-05-24

许可协议