参考文章点这里
我们都知道,在浏览器环境下,目前VueRouter一共有两种模式,分别是
- hash模式
- history模式
这两种模式的区别有如下几点:
- 长相不同,history更美观。(颜值即正义,哈哈)
- hash模式下,hash值为#后面的内容,虽然出现在URL中,但是不会被包括在http请求中,对后端无影响,不会重复加载页面。
- history模式下,前端URL必须与实际向后端发起请求的URL一致,否则返回404错误。
下面具体记录一下实现过程吧。
模式参数
vue-router中通过mode这个参数来控制路由的实现模式。
在VueRouter构造函数中,mode以参数形式传入,用来指示history的类别,history的类别有三类:HashHistory | HTML5History | AbstractHistory。
默认为hash模式,不在浏览器下运行时,会强制为abstract模式。
通过supportsPushState变量判断是否支持HTML5History。
根据mode确定类别,然后实例化并初始化。
HashHistory
#符号本身以及它后面的字符称之为hash,可通过window.location.hash属性读取。它具有如下特点:
- hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面
- 可以为hash的改变添加监听事件:
window.addEventListener("hashchange", funcRef, false)
- 每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录
HashHistory.push()
push()方法是通过对window.location.hash直接赋值,将新路由添加到浏览器访问历史的栈顶。
window.location.hash = fullpath
hash的改变会自动添加到浏览器的访问历史记录中。而视图的更新是通过Vue.mixin()方法,全局注册一个混合,影响注册之后所有创建的每个 Vue 实例,该混合在beforeCreate钩子中通过Vue.util.defineReactive()定义了响应式的_route属性。所谓响应式属性,即当_route值改变时,会自动调用Vue实例的render()方法,更新视图。
HashHistory.replace()
replace()方法是替换当前路由。通过调用window.location.replace方法将路由进行替换。
监听地址栏
以上讨论的VueRouter.push()和VueRouter.replace()是可以在vue组件的逻辑代码中直接调用的,除此之外在浏览器中,用户还可以直接在浏览器地址栏中输入改变路由,因此VueRouter还需要能监听浏览器地址栏中路由的变化,并具有与通过代码调用相同的响应行为。在HashHistory中这一功能通过setupListeners实现。该方法设置监听了浏览器事件hashchange,调用的函数为replaceHash,即在浏览器地址栏中直接输入路由相当于代码调用了replace()方法。
HTML5History
代码结构以及更新视图的逻辑与hash模式基本类似,只不过将对window.location.hash直接进行赋值和window.location.replace()方法改为了调用history.pushState()和history.replaceState()方法。
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
在HTML5History中添加对修改浏览器地址栏URL的监听是直接在构造函数中执行的:通过监听popState()事件来实现。
由于HTML5History用到了HTML5的新特性,所以存在浏览器版本兼容问题,可以通过supportsPushState变量来检查。
两种模式的比较
现在大部分文章都推荐用history模式。调用history.pushState() 有以下几点优势:
- pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL。
- pushState设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发记录添加到栈中。
- pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串。
- pushState可额外设置title属性供后续使用。
history模式的弊端
如果用户直接在地址栏中输入并回车,或者浏览器重启重新加载应用等情况下,hash模式仅改变hash部分的内容,而hash部分是不会包含在HTTP请求中的,故在hash模式下遇到根据URL请求页面的情况不会有问题。而history模式则会将URL修改得就和正常请求后端的URL一样,在此情况下重新向后端发送请求,如后端没有配置对应/user/id的路由处理,则会返回404错误。官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时这么做以后,服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果是用 Node.js 作后台,可以使用服务端的路由来匹配 URL,当没有匹配到路由的时候返回 404,从而实现 fallback。
什么情况下要用hash模式?
要想从文件系统直接加载Vue单页应用而不借助后端服务器,除了打包后的一些路径设置外,还需确保vue-router使用的是hash模式。