You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
transitionTo(location: RawLocation,onComplete?: Function,onAbort?: Function){constroute=this.router.match(location,this.current)this.confirmTransition(route,()=>{this.updateRoute(route)onComplete&&onComplete(route)this.ensureURL()// fire ready cbs onceif(!this.ready){this.ready=truethis.readyCbs.forEach(cb=>{cb(route)})}},err=>{if(onAbort){onAbort(err)}if(err&&!this.ready){this.ready=truethis.readyErrorCbs.forEach(cb=>{cb(err)})}})}
因为我们用的比较多的是 vue 的 HashHistory。下面我们首先来介绍一下 HashHistory。我们知道,通过
mode
来确定使用history
的方式,如果当前mode = 'hash'
,则会执行:this.fallback
是用来判断当前mode = 'hash'
是不是通过降级处理的:接下来我们看看
HashHistory
的内部实现,首先是看一下new HashHistory()
的时候,实例化做了哪些事:constructor
可以看到在实例化过程中主要做两件事情:针对于不支持
history api
的降级处理,以及保证默认进入的时候对应的 hash 值是以 / 开头的,如果不是则替换。如果细心点,可以发现这里并没有对
hashchange
事件做处理。主要是因为这个问题:beforeEnter fire twice on root path ('/') after async next call。简要来说就是说如果在
beforeEnter
这样的钩子函数中是异步的话,beforeEnter
钩子就会被触发两次,原因是因为在初始化的时候如果此时的 hash 值不是以 / 开头的话就会补上 #/,这个过程会触发hashchange
事件,所以会再走一次生命周期钩子,也就意味着会再次调用beforeEnter
钩子函数。transitionTo
还记得
init
的时候,有这样的动作:如果
history
是HashHistory
的实例。则调用history
的transitionTo
方法。调用transitionTo
的时候传入了3个参数,第一个是history.getCurrentLocation()
,后面的都是setupHashListener
。先来看一下getCurrentLocation
:也就是返回了当前路径。接着是
setupHashListener
函数,其内部定义了history.setupListeners()
的执行。后面我们在具体分析他所做的工作,我们现在只需要明白这几个参数的含义。接下来我们来看一下
transitionTo
的实现:该函数执行的时候,先去定义了
route
变量:我们知道
location
代表了当前的 hash 路径。那么this.current
又是什么呢?不要着急,我们找到this.current
的定义:this.current
就是START
,通过createRoute
来创建返回。注意返回的是通过Object.freeze
定义的只读对象 route。可以简单看一下大致返回的内容可能是这样的:接着,我们会调用
this.router.match
方法,来获取route
对象。来看一下match
方法:大致能看出来
match
函数执行this.macher
对象的match
方法调用。this.matcher
对象通过createMatcher
方法返回。看一下this.matcher.match
方法:这里我们可能需要理解一下
pathList
、pathMap
、nameMap
这几个变量。他们是通过createRouteMap
来创建的几个对象:routes 使我们定义的路由数组,可能是这样的:
而
createRouteMap
主要作用便是处理传入的routes
属性,整理成3个对象:nameMap
pathList
pathMap
所以
match
的主要功能是通过目标路径匹配定义的route 数据,根据匹配到的记录,来进行_createRoute
操作。而_createRoute
会根据RouteRecord执行相关的路由操作,最后返回Route对象:现在我们知道了
this.mather.match
最终返回的就是Route
对象。到这里,我们再回到之前所说的transitionTo
方法:得到正确的路由对象
route
后,我们开始跳转动作confirmTransition
。接下来看看confirmTransition
的主要操作confirmTransition
这里有一个很关键的路由对象的 matched 实例,从上次的分析中可以知道它就是匹配到的路由记录的合集;这里从执行顺序上来看有这些
resolveQueue
、extractLeaveGuards
、extractUpdateHooks
、resolveAsyncComponents
、runQueue
关键方法。我们先来看看resolveQueue
方法:1. resolveQueue
可以看出
resolveQueue
就是交叉比对当前路由的路由记录和现在的这个路由的路由记录来确定出哪些组件需要更新,哪些需要激活,哪些组件被卸载。再执行其中的对应钩子函数。2. extractLeaveGuards/extractUpdateHooks
总的来说
extractLeaveGuards
的功能就是找到即将被销毁的路由组件的beforeRouteLeave
钩子函数。处理成一个由深到浅的顺序组合的数组。接下来的extractUpdateHooks
函数功能也是类似,主要是处理beforeRouteUpdate
钩子函数。这里不再过多介绍了。3. resolveAsyncComponents
这里主要是用来处理异步组建的问题,通过判断路由上定义的组件 是函数且没有 options来确定异步组件,然后在得到真正的异步组件之前将其路由挂起。
4. runQueue
我们知道在
confirmTransition
中通过这样的方式来调度队列的执行:为
runQueue
函数 fn 参数传入了一个iterator
函数。接下来我们看看iterator
函数的执行:我们来屡一下现在主要的流程:
transitionTo
函数,先得到需要跳转路由的 match 对象route
confirmTransition
函数confirmTransition
函数内部判断是否是需要跳转,如果不需要跳转,则直接中断返回confirmTransition
判断如果是需要跳转,则先得到钩子函数的任务队列 queuerunQueue
函数来批次执行任务队列中的每个方法。iterator
来构造迭代器由用户传入next
方法,确定执行的过程大致流程便是这样,我们接下来看处理完整个钩子函数队列之后将要执行的回调是什么样的:
可以看到,处理完整个钩子函数队列之后将要执行的回调主要就是接入路由组件后期的钩子函数
beforeRouteEnter
和beforeResolve
,并进行队列执行。一切处理完成后,开始执行transitionTo
的回调函数onComplete
:可以看到,到这里,已经完成了对当前 route 的更新动作。我们之前已经分析了,在
install
函数中设置了对route
的数据劫持。此时会触发页面的重新渲染过程。还有一点需要注意,在完成路由的更新后,同时执行了onComplete && onComplete(route)
。而这个便是在我们之前篇幅中介绍的setupHashListener
:可以看到
setupListeners
这里主要做了 2 件事情,一个是对路由切换滚动位置的处理,具体的可以参考这里滚动行为。另一个是对路由变动做了一次监听window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {})
。总结
到这里,
hash
模式下的主要操作便差不多介绍完成了,接下来我们会去介绍history
模式。参考:
vue-router 源码分析-history
The text was updated successfully, but these errors were encountered: