# 前后端路由
路由可以看成是URL到函数的映射,访问URL时,路由会以URL的数据为参数,匹配相应的返回值给这个URL。
在后端路由中,进行页面切换的时候,浏览器发送不同的url请求,服务器接收到浏览器的请求时,通过解析不同的url去拼接需要的html或者模板,然后将结果返回给浏览器进行渲染。后端路由的优点是安全行更高,利于SEO
;缺点是增加了服务端的负担,并且需要reload
页面。
需要注意的是,后端路由的url地址和请求的 method
也应该是一一对应的。假设后端只配置了了一个 post
请求的路由,那么当发出一个 get
请求的时候是访问不到的。
// http://xxx.com/user/123 get请求
router.get('/user/:id', getUser) // 需要配置get请求
router.post('/user/:id', addUser)
前端路由使客户端浏览器不依赖于服务端,根据不同的URL渲染不同的视图页面。使用前端路由减轻了服务端的压力,同时,页面的切换可以不需要刷新整个页面,没有闪烁刷新,提升了体验。
PS:前端路由广泛应用于 SPA
项目中,例如由 Vue
构建的 SPA
应用,这种应用从服务端拿到的 html
只包含一个 <div id="#app"></div>
这样的入口以及一系列的 js
文件。页面通过 js
渲染出来,这就是常说的前端渲染。前端渲染将页面渲染的任务交给了浏览器,通过客户端的算力来解决页面构建。缓解了服务端的压力且页面切换体验更好,但是对 SEO
不友好。
# 前端路由实现方式
实现前端路由,需要解决的问题是:
- 在页面不刷新的前提下实现url变化
- 监听url变化,以便执行页面替换逻辑
实现前端路由有两种方式,一种是 hash
方式。这种方式利用浏览器不会对 #
后面的路径对服务端发起路由请求。也就是说,http://localhost/#/user
和 http://localhost/
这两个地址请求的都是 http://localhost
这个页面,前端再根据后面 hash
值的变化决定渲染的内容。另一种是 History
方式,这种方式使用 HTML5
新提供的 History API
。需要 IE10
以上的浏览器才支持。通过 pushState
、replaceState
改变url数据,history.go()
、history.back()
、history.forward()
等方法触发 popstate
事件进而决定页面渲染内容。
# hash
// main.js
class Routers {
constructor() {
this.routes = {} // 存放hash及对应的callback
this.currentUrl = ''
this.refresh = this.refresh.bind(this)
window.addEventListener('load', this.refresh, false)
window.addEventListener('hashchange', this.refresh, false)
}
route(path, callback) {
this.routes[path] = callback || function() {}
}
refresh() {
this.currentUrl = location.hash.slice(1) || '/'
this.routes[this.currentUrl]()
}
}
window.Router = new Routers()
const content = document.querySelector('body')
function changeColor(color) {
content.style.backgroundColor = color
}
Router.route('/', () => {
changeColor('yellow')
})
Router.route('/blue', () => {
changeColor('blue')
})
Router.route('/green', () => {
changeColor('green')
})
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hash router</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<ul>
<li>
<a href="#/">turn yellow</a>
<a href="#/blue">turn blue</a>
<a href="#/green">turn green</a>
</li>
</ul>
</body>
<script src="main.js"></script>
</html>
# history
class Routers {
constructor() {
this.routes = {}
this.bindPopSate()
}
// 初始化路由
init(path) {
history.replaceState({path: path}, null, path)
this.routes[path] && this.routes[path]()
}
route(path, callback) {
this.routes[path] = callback || function() {}
}
// 未触发 popstate 事件的路由变化
go(path, e) {
// 解决本地测试跨域问题
const fullPath = `file:///C:/Users/Administrator/Desktop/history/index.html?${e.target.getAttribute('href')}`
history.pushState({path: path}, null, fullPath)
this.routes[path] && this.routes[path]()
}
bindPopState() {
window.addEventListener('popstate', e => {
console.log(e.state)
const path = e.state && e.state.path
this.routes[path] && this.routes[path]()
})
}
}
window.Router = new Routers()
Router.init(location.pathname)
const content = document.querySelector('body')
const ul = document.querySelector('ul')
function changeColor(color) {
content.style.backgroundColor = color
}
Router.route('/', () => {
changeColor('yellow')
})
Router.route('/blue', () => {
changeColor('blue')
})
Router.route('/green', () => {
changeColor('green')
})
ul.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault()
Router.go(e.target.getAttribute('href'),e)
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>hash router</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<ul>
<li>
<a href="/">turn yellow</a>
<a href="/blue">turn blue</a>
<a href="/green">turn green</a>
</li>
</ul>
</body>
<script src="main.js"></script>
</html>
参考:
history api (opens new window)