网络原理中,路由指的是根据上一接口的数据包中的IP地址,查询路由表转发到另一个接口,它决定的是一个端到端的网络路径。
web里的话,路由概念也是类似的,解析一个‘端’URL(地址,可能包含了一些参数等)来将请求分配到指定的一个‘端’(控制器、方法等一切处理程序),客户端的请求是以URL的形式传递给服务器的(示例get方法),传统WEB开发中,URL对应服务器上某个目录下的某个文件。
而在MVC开发中,WEB 服务器会截获所有请求,不做资源存在性检查,直接转发给网站的路由程序。路由器再调用相关的控制器。控制器调用相关的服务,并返回视图对象。路由器再从视图对象中提取生成好的网页代码返回给Web服务器,最终返回给客户端。
前端路由的出现要从 ajax 开始,Ajax,全称 Asynchronous JavaScript And XML(异步的js和xml),是浏览器用来实现异步加载的一种技术方案。
在 90s 年代初,大多数的网页都是通过直接返回 HTML 的,用户的每次更新操作都需要重新刷新页面。及其影响交互体验,随着网络的发展,迫切需要一种方案来改善这种情况。
1996,微软首先提出 iframe 标签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest的前身),并在 IE5 通过 ActiveX 来实现了这项技术。在微软实现这个概念后,其他浏览器比如 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。
有了 Ajax 后,用户交互就不用每次都刷新页面,体验带来了极大的提升。
但真正让这项技术发扬光大的,还是后来的 Google Map,它的出现向人们展现了 Ajax 的真正魅力,释放了众多开发人员的想象力,让其不仅仅局限于简单的数据和页面交互,为后来异步交互体验方式的繁荣发展带来了根基。
而异步交互体验的更高级版本就是 SPA—— 单页应用。
(异步交互最高级的体验是什么?PWA(渐进式WEB应用)
什么是PWA
2019前端必会黑科技之PWA)
单页应用不仅仅是在页面交互是无刷新的,连页面跳转(如标签)都是无刷新的,为了实现单页应用,所以就有了前端路由。
单页应用的概念是伴随着MVVM出现的。最早由微软提出,然后他们在浏览器端用 Knockoutjs 实现。但这项技术的强大之处并未当时的开发者体会到,可能是因为 Knockoutjs 实现过于复杂,导致没有大面积的扩散。
同样,这次接力的选手依然是 Google。Google 通过 Angular.js 将 MVVM 及单页应用发扬光大,让前端开发者能够开发出更加大型的应用,职能变得更大了。随后都是大家都知道的故事,前端圈开始得到了爆发式的发展,陆续出现了很多优秀的框架如vue,react。
https://github.com/hwen/blogSome/issues/2
前端路由的实现其实很简单。
从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下):
记录当前页面的状态(保存或分享当前页的url,再次打开该url时,网页还是保存(分享)时的状态);
可以使用浏览器的前进后退功能(如点击后退按钮,可以使页面回到使用ajax更新页面之前的状态,url也回到之前的状态);
本质上就是监测 url 的变化,截获 url 地址,然后解析来匹配路由规则。
而url 每次变化都会刷新页面,页面都刷新了,JavaScript 怎么检测和截获 url?
基于hash
在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于
>https://www.xxxxx.com/x/#articleHeader2
这种 ,#后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。
让我们来整理思路,假如我们要用 hash 的模式实现一个路由,那么流程应该是这样的。
hash 的实现相对来说要简单方便些,而且不用服务器来支持。
vue-router hash 实现源码地址
/**
* 添加 url hash 变化的监听器
*/
setupListeners () {
const router = this.router
/**
* 每当 hash 变化时就解析路径
* 匹配路由
*/
window.addEventListener('hashchange', () => {
const current = this.current
/**
* transitionTo:
* 匹配路由
* 并通过路由配置,把新的页面 render 到 ui-view 的节点
*/
this.transitionTo(getHash(), route => {
replaceHash(route.fullPath)
})
})
}
网易云音乐,百度网盘就采用了hash路由,看起来就是这个样子:
http://music.163.com/
https://pan.baidu.com/
借鉴: https://github.com/hwen/blogSome/issues/2
export class HTML5History extends History {
constructor (router, base) {
super(router, base)
/**
* 原理还是跟 hash 实现一样
* 通过监听 popstate 事件
* 匹配路由,然后更新页面 DOM
*/
window.addEventListener('popstate', e => {
const current = this.current
// Avoiding first `popstate` event dispatched in some browsers but first
// history route not updated since async guard at the same time.
const location = getLocation(this.base)
if (this.current === START && location === initLocation) {
return
}
this.transitionTo(location, route => {
if (supportsScroll) {
handleScroll(router, route, current, true)
}
})
})
}
go (n) {
window.history.go(n)
}
push (location, onComplete, onAbort) {
const {
current: fromRoute } = this
this.transitionTo(location, route => {
// 使用 pushState 更新 url,不会导致浏览器发送请求,从而不会刷新页面
pushState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
replace (location, onComplete, onAbort) {
const {
current: fromRoute } = this
this.transitionTo(location, route => {
// replaceState 跟 pushState 的区别在于,不会记录到历史栈
replaceState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
}
借鉴:https://github.com/hwen/blogSome/issues/2
1、hash 就是指 url 尾巴后的 # 号以及后面的字符,history没有底带#,外观上比hash 模式好看些;
2、原理的区别(原理)
3、hash 能兼容到IE8, history 只能兼容到 IE10;
4、hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件(hashchange只能改变 # 后面的url片段);虽然hash路径出现在URL中,但是不会出现在HTTP请求中,对后端完全没有影响,因此改变hash值不会重新加载页面。而基于History API的路由,会在页面刷新时向服务器发送请求,会重新加载界面所以需要对服务器做一些改造,需要对不同的路由进行相应的设置才行。
5、history的pushState()设置新的URL可以是任意与当前URL同源的URL,而hash只能改变#后面的内容,因此只能设置与当前URL同文档的URL
6、history的pushState()设置的URL与当前URL一模一样时也会被添加到历史记录栈中,而hash模式中,#后面的内容必须被修改才会被添加到新的记录栈中
7、history的pushState()可以通过stateObject参数添加任意类型的数据到记录中,而hash只能添加短字符串
8、history的pushState()可额外设置title属性供后续使用