前端路由原理

概述

  • 稍微复杂一点的 spa ,都需要路由
  • vue-router 是 vue 全家桶的标配之一
  • 属于和日常使用相关的原理,面试常考

常用路由模式

hash

网页 url 组成部分

1
2
3
4
5
6
7
location.protocol // http:
location.hostname // 127.0.0.1
location.host // 127.0.0.1:8080
location.port // 8080
location.pathname // /index.html
location.search // ?a=1&b=2
location.hash // #/aaa/bbb

特点

  • hash 变化会触发网页跳转,即浏览器的前进、后退
  • hash 变化不会刷新页面,spa需要的特点
  • hash 永远不会提交到 server 端

代码

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hash test</title>
</head>
<body>
<p>hash test</p>
<button id="btn1">修改 hash</button>

<script>
// hash 变化,包括:
// a. JS 修改 url
// b. 手动修改 url 的 hash
// c. 浏览器前进、后退
window.onhashchange = (event) => {
console.log('old url', event.oldURL)
console.log('new url', event.newURL)

console.log('hash:', location.hash)
}

// 页面初次加载,获取 hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash)
})

// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user'
})
</script>
</body>
</html>

H5 history

特点

  • 用 url 规范的路由,但跳转时不刷新页面
  • history.pushState
  • window.onpopstate

代码演示

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
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>history API test</title>
</head>
<body>
<p>history API test</p>
<button id="btn1">修改 url</button>

<script>
// 页面初次加载,获取 path
document.addEventListener('DOMContentLoaded', () => {
console.log('load', location.pathname)
})

// 打开一个新的路由
// 【注意】用 pushState 方式,浏览器不会刷新页面
document.getElementById('btn1').addEventListener('click', () => {
const state = { name: 'page1' }
console.log('切换路由到', 'page1')
history.pushState(state, '', 'page1') // 重要!!
})

// 监听浏览器前进、后退
window.onpopstate = (event) => { // 重要!!
console.log('onpopstate', event.state, location.pathname)
}

// 需要 server 端配合,可参考
// https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90

// 服务端无论路由是谁都返回index,其他跳转都在前端处理
// const http = require('http')
// const fs = require('fs')
// const httpPort = 80

// http.createServer((req, res) => {
// fs.readFile('index.htm', 'utf-8', (err, content) => {
// if (err) {
// console.log('We cannot open "index.htm" file.')
// }

// res.writeHead(200, {
// 'Content-Type': 'text/html; charset=utf-8'
// })

// res.end(content)
// })
// }).listen(httpPort, () => {
// console.log('Server listening on: http://localhost:%s', httpPort)
// })

</script>
</body>
</html>

两者选择

  • toB 系统推荐用 hash,简单易用,对url不敏感
  • toC 系统,可以考虑 H5 history ,但需要服务端支持
  • 能选择简单的就不选择复杂的