路由
history API javascript标准参考教程(alpha) 前端路由简介 https://juejin.im/post/5ac61da66fb9a028c71eae1b
后端路由
出现SPA大宝剑前,一般路由指的就是后端路由,服务器根据不同的请求返回不同的资源。
一般就是浏览器发出请求(url变化)--> 服务器监听80(443)端口的请求,解析url --> 根据服务器的路由配置,返回相应的信息(html, json等)--> 浏览器根据content-type决定如何解析。
// 这里有段koa的路由
module.exports = function(){
var router = new Router({
prefix: '/api'
})
router.post('/u/signup', App.hasBody, User.signup)
router.post('/u/update', App.hasBody, App.hasToken, User.update)
router.get('/test/user/users',User.users)
router.post('/test/user/add',User.addUser)
router.post('/test/user/delete',User.deleteUser)
return router
}
前端路由
匹配不同的URL,进行解析,动态渲染出部分区域的HTML。
hash路由
上古WEB自从有个ajax ,整个WEB世界就变得越来越有意思了,ajax 是可以在不刷新浏览器的情况下异步请求交互,之后出现的SPA,更是可以完成无刷新的情况下实现路由跳转。
www.gebilaowang.com/#/helloworld
原理 url中 #后面的hash值的变化不会像服务器发出请求,也就不会刷新页面。当URL的片段标识符更改时,还会触发hashchange事件 (跟在#符号后面的URL部分,包括#符号)。我们监听浏览器的hashchange时间来做相应的路由操作。当我们刷新页面的时候,也不会像浏览器发请求。
//mini hashhistory路由
//
class hashRouter {
constructor() {
//存储路由
this.routes = {}
this.currentUrl = ''
this.refresh = this.refresh.bind(this);
window.addEventListener('load', this.refresh, false);
window.addEventListener('hashchange', this.refresh, false);
}
// 存储路由的hash以及对应的callback,
route(path, callback = function(){}) {
this.routes[path] = callback
}
//刷新
refresh() {
this.currentUrl = location.hash.slice(1) || '/'
// 执行当前路由对应的callback
this.routes[currentUrl]()
}
}
history路由
HTML5标准中引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。我们可以通过这个来实现另一种方式的前端路由。 history路由没有 #,更加美观,而且因为没有#, 用户刷新页面之类的操作时,还是会 像服务器发送请求。(hash路由并不会) 假如RD没有响应的配置,经常会出现404,所以这种方案需要服务器的支持, 所有的路由都重定向到根模板
- history.pushState()
假如当前是 www.gblw.com/1.html
var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');
执行之后浏览器显示的是 www.gblw.com/2.html,但是并不会跳转到2.html,也不是检查2.html是否存在。只是成为浏览器历史中的一个记录。
window.history.pushState(state, title, url)
- state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。
- title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
- url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
pushState()方法不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有反应。
history.replaceState() 修改history当前的记录,其他与pushState一样。
popstate事件
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
});
每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。
注意 - 仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件,只有用户 点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用 **History.back()、History.forward()、History.go()**方法时才会触发。 - 该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。 - 页面第一次加载的时候,浏览器不会触发popstate事件。 - popstate事件的回调函数的参数event对象。他的state属性执行pushState和replaceState方法为当前URL所提供的状态对象,(即两个方法的第一个参数),也可以通过history对象读取。
// mini history路由
class HistoryRouter {
constructor() {
// 存储路由
this.routes = {}
// 初始化的时候监听popstate事件
this._bindPopState()
}
// 初始化路由
// 页面第一次加载的时候,浏览器不会触发popstate事件,我们需要init时replaceState
init(path) {
history.replaceState({path}, null, path)
this.routes[path] && this.routes[path]()
}
// 路径和回调对应
route(path, callback = function(){}) {
this.routes[path] = callback
}
// 触发路由对应的回调
go(path) {
history.pushState({path}, null, path)
this.routes[path] && this.routes[path]()
}
//监听popstate事件
_bindPopState() {
window.addEventListener('popstate', e => {
// 当点击浏览器倒退按钮和前进按钮或者 执行history.go/forward/back等方法时,
// 拿到对应路由下存储的状态,执行其回调
const path = e.state && e.state.path
this.routes[path] && this.routes[path]()
})
}
}
原理很简单:就是通过 pushState,replaceState API,将对应的状态以及相应的回调存储下来。在用popstate来做相应的配合。 当匹配到相应路由的状态的时候,执行相应的回调。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<ul>
<li><a href="/">home </a></li>
<li><a href="/gblw">gblw </a></li>
<li><a href="/gbll">gbll </a></li>
</ul>
<div id='body'></div>
<script>
let body = document.querySelector("#body")
let router = new HistoryRouter()
// 注册路由
router.route('/',e => {
body.innerHTML = '<div>home</div>'
})
router.route('/gblw',e => {
body.innerHTML = '<div>gblw</div>'
})
router.route('/gbll', e => {
body.innerHTML = '<div>gbll</div>'
})
// 初始化路由 要在注册路由之后
// router.init(location.pathname)
router.init('/')
document.querySelector("ul").addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
router.go(e.target.getAttribute('href'));
}
},false);
</script>
</body>
</html>
了解路由的前端路由的原理,能让我们更好地理解现代前端开发中的路由控制,还能让我们更好地跟RD沟(che)通(pi), 例如
- 58有个生成url二维码功能,所以需要我们对雇主盛典项目从hash路由改进到history,需要跟RD沟通怎么去配置模板...
- 春运项目3.1下线,需要将将活动主会场链接到分会场, RD对 /index请求做了拦截,但是 分会场有个返回主会场的按钮,点击返回到index还是会跳到主会场,因为第一次请求分会场/branch,RD会去做重定向 到我们的index模板,当我们从branch跳转到index的时候,其实并没有想后端去做请求,也就没办法去控制,这部分路由的控制权在我们手里。