Vue-Router的两种路由模式
H5 History模式官网描述vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。const router = new VueRout
对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义。前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。
为了达到这一目的,浏览器当前提供了以下两种支持:
- hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:
http://www.abc.com/#/hello
,hash 的值为#/hello
。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。 - history —— 利用了 HTML5 History Interface 中新增的
pushState()
和replaceState()
方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的back
、forward
、go
的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
因此可以说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。
Hash模式
hash,即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:http://www.abc.com/#/hello
,hash 的值为 #/hello
。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。 换句话说,#
后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。
每次 hash 值的变化,会触发hashchange
这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange
来实现更新页面部分内容的操作。hash模式背后的原理是onhashchange
事件,可以在window
对象上监听这个事件:
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1);
document.body.style.color = hash;
}
// 上面的代码可以通过改变hash来改变页面字体颜色。
当你选择mode类型之后,程序会根据你选择的mode 类型创建不同的history对象(hash:HashHistory 或 history:HTML5History 或 abstract:AbstractHistory)
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
hash模式会创建hashHistory
对象,这个对象有两个方法:HashHistory.push()
和HasHistory.replace()
。在访问不同的路由的时候,会发生两件事:HashHistory.push()
将新的路由添加到浏览器访问历史的栈顶,和HasHistory.replace()
替换到当前栈顶的路由。
因为hash发生变化的url都会被浏览器记录(历史访问栈)下来,从而你会发现浏览器的前进后退都可以用了。这样一来,尽管浏览器没有请求服务器,但是页面状态和url一一关联起来。
H5 History模式
const router = new VueRouter({
mode: 'history',
routes: [...]
})
因为HTML5标准发布,多了两个 修改历史状态的API:pushState() 和 replaceState()。通过这两个 API (1)可以改变 url 地址且不会发送请求,(2)不仅可以读取历史记录栈,还可以对浏览器历史记录栈进行修改。
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
除此之外,还有popState()。当浏览器跳转到新的状态时,将触发popState事件。
切换历史状态的API包括back,forward,go三个方法,对应浏览器的前进forward,后退back,跳转go操作。有同学说了,(谷歌)浏览器只有前进和后退,没有跳转,在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转(也许叫跳更合适):
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
使用场景
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
根据 Mozilla Develop Network 的介绍,调用 history.pushState()
相比于直接修改 hash,存在以下优势:
pushState()
设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改#
后面的部分,因此只能设置与当前 URL 同文档的 URL;- pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
- pushState() 通过
stateObject
参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串; - pushState() 可额外设置
title
属性供后续使用。
当然啦,history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。
hash
模式下,仅 hash 符号之前的内容会被包含在请求中,如http://www.abc.com
,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。history
模式下,不怕前进,不怕后退,就怕刷新,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。前端的 URL 必须和实际向后端发起请求的 URL 一致,如http://www.abc.com/book/id
。如果后端缺少对/book/id
的路由处理,将返回 404 错误。
vue-router官网如此描述:不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。 所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
总之,当用户刷新页面之类的操作时,浏览器会给服务器发送请求,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
参考资料
[1] https://www.cnblogs.com/ceceliahappycoding/p/10552620.html
更多推荐
所有评论(0)