总结
在VueRouter中提供了两种模式:
- hash模式
- history模式
hash模式的基础是当URL的#
后的参数改变时浏览器不会发送请求
history模式的基础是pushState
和replaceState
修改浏览器的历史栈后浏览器不会立即发送请求
前端路由
前端路由是通过改变URL,在不重新刷新整体页面的情况下,更新页面视图。
目前浏览器实现这一功能主要通过两种方式:
- 利用URL中的hash值
- 利用H5中的
history
对象
Hash模式
vue-router默认使用的是hash模式,使用URL的hash来模拟一个完整的URL,当URL改变时,页面不会重新加载。
#
的含义
#
代表网页中的位置,其莜面的字符,就是该位置的标志符号
http://www.example.com/index.html#print
浏览器读取这个URL周,会自动将print
位置滚动至可视区域,对应print
位置可以通过两种方法实现:使用锚点<a name="print"></a>
或者使用id属性<div id="print"></div>
#
的特性
#
是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#
及后面的内容字符
单单改变#
后面的部分,只会可能触发浏览器的滚动,不会重新加载网页。
改变了#
后面的部分,都会改变浏览器的访问历史,使用后退按钮,就可以回到上一个位置,这对于ajax应用程序特别有用,可以用不同的#值,表示不同的访问状态,然后向用户给出可以访问某个状态的链接。
使用
通过window.location.hash
可以读取、写入页面的hash值,读取时,可以用来判断网页状态是否改变;写入时,则会在不重载网页的前提下,创造一条访问历史记录。
HTML5中增加的onhashchange
事件,当#
发生变化时,就会触发这个事件
VueRouter中的实现
首先构建了HashHistory
构造函数,获取页面的hash值
然后定义了HashHistory.push()
方法,当页面的hash值发生变化时,会替换window.location.hash
,hash的改变会自动添加到浏览器的访问历史记录中,视图的更新是首先通过Vue.mixin()
方法,全局注册一个混合,定义了响应式的_route
属性,当_route
改变时会触发Vue实例的render
方法,更新视图
graph TB
$router.push()-->HashHistory.push()
HashHistory.push()-->History.transitionTo()
History.transitionTo()-->History.updateRoute()
History.updateRoute()-->{app._route=route}
{app._route=route}-->vm.render()
HashHistory.replace()
与push()
方法不同之处在于,它并不是将新路由添加到浏览器访问历史栈顶,而是替换掉当前的路由
上面的VueRouter.push()
和VueRouter.replace()
是可以在vue组件的逻辑代码中直接调用的,除此之外在浏览器中,用户还可以直接在浏览器地址栏中输入改变路由,因此还需要监听浏览器地址栏中路由的变化 ,并具有与通过代码调用相同的响应行为,在HashHistory中这一功能通过setupListeners
监听hashchange
实现
详细的源码解读看这篇文章。
HTML History模式
History interface是浏览器历史记录栈提供的接口,通过back()
,forward()
,go()
等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
history.pushState()
和history.replaceState()
方法,分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate
配合使用。
window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
stateObject
:当浏览器跳转到新的状态时,将触发popState
事件,该事件将携带这个stateObject
参数的副本title
:所添加记录的标题url
:所添加记录的url(可选的)
pushState
和replaceState
两种方法的共同特点:当调用修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础。
VueRouter中使用History模式
VueRouter也提供了Hsitory模式,利用的是H5中的history
对象和事件,只需要在新建VueRouter的实例时传入mode
参数:
const router = new VueRouter({
mode: 'history',
routes: [...]
})
这种模式下页面的URL就像正常的url,例如http://yoursite.com/user/id
这种模式需要后台配置支持,如果后台没有正确的配置,当用户在浏览器直接访问http://oursite.com/user/id
就会返回404,所以需要在服务端增加一个覆盖所有情况的候选资源:如果URL匹配不到任何静态资源,则应该返回同一个index.html
页面,这个页面就是app依赖的页面。
注意:
这么做以后,服务器就不再返回404错误页面,因为对于所有路径都会返回index.html
文件。为了避免这种情况,应该在Vue应用里面覆盖所有的路由情况,然后在给出一个404页面。
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})
VueRouter中的实现
代码结构以及更新视图的逻辑与hash模式基本类似,只不过将对window.location.hash()
直接进行赋值window.location.replace()
改为了调用history.pushState()
和history.replaceState()
方法。
两种模式的比较
一般的需求场景中,hash模式与history模式是差不多的,根据MDN的介绍,调用history.pushState()
相比于直接修改hash主要有以下优势:
pushState
设置的新url可以是与当前url同源的任意url,而hash只可修改#
后面的部分,故只可设置与当前同文档的urlpushState
设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发记录添加到栈中pushState
通过stateObject
可以添加任意类型的数据记录中,而hash只可添加短字符串pushState
可额外设置title
属性供后续使用