路由响应过程:
-
浏览器发出请求
-
服务器监听到num端口(或443)有请求过来,并解析url路径
-
-
浏览器根据数据包的 Content-Type 来决定如何解析数据
let router = new Router({
mode: 'history|hash|abstract',
routes: [
{ // 默认页
path: '*',
redirect: to => {
return '/'
},
meta: {
status: ***
}
},
{
path: '/',
name: '****',
component: ****,
meta: {
status: ***
}
},
],
beforeEnter: (to, from, next) => {}),
scrollBehavior: fun()
})
在使用vueRouter的时候,我们会在项目中使用Vue.use(Router)安装,它会加载VueRouter中的 install 方法使得所有组件都可以使用router的实例(this.$router/this.$route) 在install的时候实际上完成一些初始化的工作
(源码install.js)
1)在vue.prototype上注册一个$router/$route的实例
Vue.mixin({
beforeCreate () {
if (isDef(this.$options.router)) {
this._routerRoot = this
this._router = this.$options.router
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
2)全局注册RouterView, RouterLink这两个组件
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
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.js)
// supportsPushState用于用于判断浏览器是否支持h5
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
const current = this.current
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
if (supportsScroll) {
handleScroll(this.router, route, current, true)
}
if (!supportsPushState) { // 如果不支持pushState 就使用window.location.replace(getUrl(path))
replaceHash(route.fullPath)
}
})
})
b)history
在h5之后,得到浏览器的支持就可以使用history模式了,在history模式下路由的改变会触发popstate事件,因此history模式原理也是对popstate事件的监听,然后做出相应操作。
(源码html5.js)
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)
}
})
})
c)abstract
abstract 模式是用于原生开发(非浏览器typeof window=== 'undefined')的情况下,这就意味着不能使用window对象;另外由于在原生app中,页面储存一个数组栈中,它记录着页面的的访问记录,因此在js中最简单的就是定义数组进行模仿。
(源码abstrack.js)
constructor (router: Router, base: ?string) {
super(router, base)
this.stack = [] // 定义一个stack数组,用于存储路由
this.index = -1 // 记录每个路由在stack中的索引
}
3)history.pushState()/replaceState() 会修改url ,不会使得页面更新,也不会触发window.popstate事件。
在history模式下使得页面url与代码中路由地址相同
4)history.back() /go(),window.location.hash 都会使得页面改变,触发window.popstate事件。
对此事件进行拦截处理一些事情(参数接收,页面位置记录)
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
const current = this.current
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
if (supportsScroll) {
handleScroll(this.router, route, current, true)
}
if (!supportsPushState) {
replaceHash(route.fullPath)
}
})
})
5)使用导航的时候其实都是使用的 this.history.push()/ go() /replace() 进行页面切换。
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)
}
go (n: number) {
this.history.go(n)
}
back () {
this.go(-1)
}
forward () {
this.go(1)
}
https://segmentfault.com/a/1190000015123061