前端路由原理解析和实现

路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。
在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。

前端路由的两种模式:

  • hash-router
  • history-router

hash-router

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。
location.hash=anchorname

hashchange

The hashchange event is fired when the fragment identifier of the URL has changed (the part of the URL beginning with and following the # symbol).
URL 片段标识符(开头的 URL 的一部分和#号后的部分)发生改变,会触发 hashchange 事件。

实现:

  1. 通过浏览器前进后退改变 url、通过标签改变 url、通过 window.url,这几种情况改变 url。
  2. 通过 hashchange 事件监听 URL 的变化,触发 hashchange 事件。
  3. 在 hashchange 事件中实现路由切换。
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
<body>
<ul>
<!-- 定义路由 -->
<li><a href="#/home">home</a></li>
<li><a href="#/about">about</a></li>

<!-- 渲染路由对应的 UI -->
<div id="routeView"></div>
</ul>
</body>
<script>
// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad);
// 监听路由变化
window.addEventListener('hashchange', onHashChange);

// 路由视图
var routerView = null;

function onLoad() {
routerView = document.querySelector('#routeView');
onHashChange();
}

// 路由变化时,根据路由渲染对应 UI
function onHashChange() {
console.log('onHashChange', location.hash);
switch (location.hash) {
case '#/home':
routerView.innerHTML = 'Home';
return;
case '#/about':
routerView.innerHTML = 'About';
return;
default:
return;
}
}
</script>

history-router

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
<body>
<ul>
<li><a href="/home">home</a></li>
<li><a href="/about">about</a></li>

<div id="routeView"></div>
</ul>
</body>

<script>
// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad);
// 监听路由变化
window.addEventListener('popstate', onPopState);

// 路由视图
var routerView = null;

function onLoad() {
routerView = document.querySelector('#routeView');
onPopState();

// 拦截 <a> 标签点击事件默认行为, 点击时使用 pushState 修改 URL并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
var linkList = document.querySelectorAll('a[href]');
linkList.forEach(el =>
el.addEventListener('click', function(e) {
e.preventDefault();
history.pushState(null, '', el.getAttribute('href'));
onPopState();
})
);
}

// 路由变化时,根据路由渲染对应 UI
function onPopState() {
switch (location.pathname) {
case '/home':
routerView.innerHTML = 'Home';
return;
case '/about':
routerView.innerHTML = 'About';
return;
default:
return;
}
}
</script>

参考:
前端路由原理解析和实现