通过改变URL,在不重新请求页面的情况下,更新页面视图。
更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:
mode参数
Vue中,通过mode这一参数来决定采用哪一种方式,选择流程如下:
默认为hash
使用history时,进行如下设置(如果浏览器比支持history新特性,则采用hash方式)
const router=new VueRouter({
mode:'history',//设置路由实现方式为history
routes:[...]
})
创建VueRouter的实例对象时,mode以构造参数的形式传入,如下代码:
src/index.js
export default class VueRouter{
mode: string; // 传入的字符串参数,指示history类别
history: HashHistory | HTML5History | AbstractHistory; // 实际起作用的对象属性,必须是以上三个类的枚举
fallback: boolean; // 如浏览器不支持,'history'模式需回滚为'hash'模式
constructor (options: RouterOptions = {}) {
let mode = options.mode || 'hash' // 默认为'hash'模式
this.fallback = mode === 'history' && !supportsPushState // 通过supportsPushState判断浏览器是否支持'history'模式
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract' // 不在浏览器环境下运行需强制为'abstract'模式
}
this.mode = mode
// 根据mode确定history实际的类并实例化
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}`)
}
}
}
init (app: any /* Vue component instance */) {
const history = this.history
// 根据history的类别执行相应的初始化操作和监听
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
history.listen(route => {
this.apps.forEach((app) => {
app._route = route
})
})
}
// VueRouter类暴露的以下方法实际是调用具体history对象的方法
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.push(location, onComplete, onAbort)
}
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.replace(location, onComplete, onAbort)
}
}
源码
hash(“#”)的作用是加载URL中指示的网页的位置。
#
本身以及它后面的字符,可以通过window.location.hash
获取。
#
http://localhost:8080/#/a
因此改变hash不会重新加载页面
hash(window.localtion.hash)
,都会在浏览器访问历史中增加一个记录。http://localhost:8080/#/a
//变为 如下地址,浏览器访问历史中会增加一个记录
http://localhost:8080/#/b
window.addEventListener("hashchange",funcRef,false)
利用hash的以上特点,就可以来实现“前端路由”更新视图但不重新请求页面的功能了。
HashHistory 拥有两个方法,一个是HashHistory.push(), 一个是HashHistory.replace()
将路由添加到浏览器访问历史的栈顶
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
详解:
$router.push() //调用方法
HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash = XXX)
History.transitionTo() //监测更新,更新则调用History.updateRoute()
History.updateRoute() //更新路由
{app._route= route} //
vm.render() //更新视图
replace()方法与push()方法不同之处在于,他并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前路由。
History interface是浏览器历史记录栈提供的接口,通过back()
,forward()
,go()
等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从Html5开始,History interface提供了两个新的方法:pushState()
,replaceState()
提供了2个新方法:pushState(),replaceState()使得我们可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
参数分析:
Popstate
事件,该事件将携带这个stateObject参数的副本#
,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。http://localhost:8080/a
为了避免这种情况,所以history路由实现方式需要服务器的支持,需要把所有的路由都定向到根页面。
在HTML5History的构造函数中监听使用popState(window.onpopstate)
与hash模式类似,只是将window.hash()
改为history.pushState()
与hash模式类似,只是将window.replace()
改为history.replaceState()
pushState设置的新的URL可以是与当前URL同源的任意URL;而hash只可修改#
后边的部分,故只可设置与当前文档同文档的URL
pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
pushState可额外设置title属性后供后续使用
history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误