Maison > Article > interface Web > Vous guide à travers la garde de navigation de vue-Router
Le contenu de cet article a pour but de vous guider à travers la garde de navigation de vue-Router. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Dans cet article, je vais régler pour vous les choses suivantes,
Comme mentionné précédemment, l'attribut history d'une instance de routeur de contenu nous aide à effectuer toutes les parties de saut, de sorte que le contenu de la garde de navigation est également dans l'historique.
Jetons un coup d'œil à cette méthode push en utilisant la classe HTML5History,
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { const { current: fromRoute } = this this.transitionTo(location, route => { pushState(cleanPath(this.base + route.fullPath)) handleScroll(this.router, route, fromRoute, false) onComplete && onComplete(route) }, onAbort) }
push ($ quand on saute le routeur .push est cette méthode), transitionTo est appelée pour compléter une série de contenu de saut, mais cette méthode n'existe pas dans la classe HTML5. La méthode
transitionTo héritée de la classe base.js consiste à implémenter la méthode de transfert de routage.
Le processus principal de transitionTo est une combinaison de la méthode confirmTranstion et de la méthode uodateRoute Traduit en mandarin : le saut d'itinéraire doit d'abord passer par un processus de saut de confirmation, puis effectuer une opération de mise à jour de l'itinéraire une fois le processus de confirmation terminé
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) { // 获取要跳转的并且经过处理的路由 const route = this.router.match(location, this.current) // confirmTranstion确认跳转过程 this.confirmTransition(route, () => { // 确认完毕后完成更新路由操作 this.updateRoute(route) onComplete && onComplete(route) this.ensureURL() // fire ready cbs once if (!this.ready) { this.ready = true this.readyCbs.forEach(cb => { cb(route) }) } }, err => { if (onAbort) { onAbort(err) } if (err && !this.ready) { this.ready = true this.readyErrorCbs.forEach(cb => { cb(err) }) } }) }Que fait confirmTransiton ? Déterminez d’abord si vous êtes sur le même itinéraire. Si tel est le cas, ne faites rien. Dans la deuxième étape, nous commencerons à collecter une vague de gardes, puis à collecter les gardes, puis à exécuter chaque garde une fois. La confirmationTransition sera exécutée avec succès. Voici quelques captures d'écran du code source :
Quelles sont les difficultés de ce processus ? Comment rassembler les gardes pour former une file d'attente de gardes ? Comment exécuter les gardes en séquence tout en annulant la file d'attente des gardes à tout moment ? Comment trouver le nœud après l'exécution de la file d'attente de garde (vous pouvez nous avertir après l'exécution de la file d'attente de garde)Une fonction runQueue est encapsulée dans vue-router pour résoudre les trois problèmes ci-dessus . deux. La première question implique un gros chapitre sur le routage du traitement vue-router. Concentrons-nous sur la fonction runQueue L'idée de la fonction runQueue : 1 : Mode itérateur pour assurer la traversée du file d'attente Chaque étape est contrôlable. 2 : Exécuter la fonction de rappel correspondante une fois la file d'attente terminée Déduire la fonction correspondante des paramètres de la fonction : file d'attente : doit. être exécuté Guard queue fn : fonction itérateur. Chaque garde de la file d'attente de garde exécute la fonction itérateur fn Le deuxième paramètre de fn fait entrer l'itérateur dans l'étape suivante. non utilisé, Ne passera pas à l'étape suivante (très important) cb : La fonction de rappel appelée à la fin
export function runQueue (queue: Array<?NavigationGuard>, fn: Function, cb: Function) { const step = index => { // 队列里已经没有内容可以执行了,那就代表队列执行完成了 if (index >= queue.length) { cb() } else { // 如果队列内容存在就执行迭代函数 if (queue[index]) { fn(queue[index], () => { step(index + 1) }) // 什么也没有那就到下一步了 } else { step(index + 1) } } } // 启动了 step(0) }runQueue suffit à nous aider à résoudre le problème de file d'attente de garde traitement. (Dépêchez-vous, cette fonction est géniale !) Nous avons fabriqué le gros marteau pour gérer la file d'attente de garde et pouvons commencer à travailler dessus. Qu'en est-il de votre file d'attente de garde ? ? Oui, oui, il y a encore une file de gardes à récupérer.
En ce moment, nous devons réfléchir à quel genre de gardes il existe ?
// 拿到路由跳转中更新、摧毁、激活时对应展示的组件。 const { updated, deactivated, activated } = resolveQueue(this.current.matched, route.matched) // 路由守卫 const queue: Array<?NavigationGuard> = [].concat( // in-component leave guards extractLeaveGuards(deactivated), // global before hooks this.router.beforeHooks, // in-component update hooks extractUpdateHooks(updated), // in-config enter guards activated.map(m => m.beforeEnter), // async components resolveAsyncComponents(activated) )L'ordre d'une file d'attente :
保证在守卫中可以停止并且跳转到其余路由,
保证守卫可以正常通过,
const iterator = (hook: NavigationGuard, next) => { if (this.pending !== route) { return abort() } try { hook(route, current, (to: any) => { // 传个false就直接执行路由的错误处理,然后停止什么都不做。 if (to === false || isError(to)) { // next(false) -> abort navigation, ensure current URL this.ensureURL(true) abort(to) } else if ( // 如果我们接受了一个可以操作的路径。 typeof to === 'string' || (typeof to === 'object' && ( typeof to.path === 'string' || typeof to.name === 'string' )) ) { // next('/') or next({ path: '/' }) -> redirect abort() // 我们就执行路由跳转操作,并且守卫队列停止下面的迭代 if (typeof to === 'object' && to.replace) { this.replace(to) } else { this.push(to) } } else { // confirm transition and pass on the value // 接续迭代下去咯 next(to) } }) } catch (e) { abort(e) } }
next函数,之前在将runQueue的函数的时候,fn接收第二个参数(之前画过重点),第二个参数的回调函数是完成迭代器向下一步执行的功能。
下面会有一点乱:
所有的前置守卫都接收三个参数
beforeEnter(to,from,next)=>{ //这个next就是我们看到的 hook里面接收的箭头函数((to:any)=>{}) //这个箭头函数里面对迭代器的next进行了一下掉用, //保证在一定情况下迭代器可以向下走一步。 next('/index') // 我们在这种next('/index')传递一个可以执行的路径时,(to:any)=>{} //这个箭头函数并不会调用迭代的next,而是跳转别的路径执行了push操作。 // 如果我们不掉用守卫中的next,迭代器的next肯定并不会执行,守卫的迭代就停止了, // 守卫堵塞confirmTransition并不会执行完毕,也就不会由后面的更细路由操作了。 }
runQueue(queue, iterator, () => { const postEnterCbs = [] const isValid = () => this.current === route // wait until async components are resolved before // extracting in-component enter guards const enterGuards = extractEnterGuards(activated, postEnterCbs, isValid) const queue = enterGuards.concat(this.router.resolveHooks) runQueue(queue, iterator, () => { if (this.pending !== route) { return abort() } this.pending = null onComplete(route) if (this.router.app) { this.router.app.$nextTick(() => { postEnterCbs.forEach(cb => { cb() }) }) } }) })
我们在把第一个queue(四个守卫与一个异步组件的加载)执行完毕后,要收集与执行第二个queue了,
第二个queue:
收集了被的激活组件内的进入守卫
全局的beforeResolve的守卫
收集完开始执行第二个queue的迭代。第二个queue执行完执行一下onComplete函数,代表着confirmTransition方法执行完毕了。确认路由的过程结束了,
下面就是updateRoute的过程。updateRoute的时候执行全部的后置守卫,因为更新路由之后,当前的路由已经变化了,所以在给守卫传参数的时候缓存了一下,之前的路由。
updateRoute (route: Route) { const prev = this.current this.current = route this.cb && this.cb(route) this.router.afterHooks.forEach(hook => { hook && hook(route, prev) }) }
所以为什么afterEach没有next呢?因为afterEach根本不在迭代器之内,他就没有next来触发迭代器的下一步。
最后我们说一下beforeEach的内容:
我们设置beforeEach全局守卫的时候,守卫们存储在哪里?
beforeEach (fn: Function): Function { return registerHook(this.beforeHooks, fn) } function registerHook (list: Array<any>, fn: Function): Function { list.push(fn) // 返回值是一个function return () => { const i = list.indexOf(fn) if (i > -1) list.splice(i, 1) } }</any>
这段代码beforeEach是通过注册守卫的方式,将注册的全局前置守卫放在beforeHooks的容器内,这个容器里面装载着所有的前置守卫
一家人(全局的 前置进入、前置resolve、后置守卫)整整齐齐的放在对应的容器里面,容器是个数组,所以注册全局守卫的时候,是支持注册多个的,
router.beforeEach(()=>{xxx}); router.beforeEach(()=>{yyy}); // 这两个守卫都会执行,只是先注册的先执行, // registerHook这个方法还可以清除对应的守卫,这个方法也可以使用
我们来回答一下开篇的5个问题
beforeRouteLeave
next的作用,使导航守卫队列的继续向下迭代
afterEach根本不在导航守卫队列内,没有迭代的next
beforeEach是可以叠加的,所有的全局前置守卫按顺序存放在beforeHooks的数组里面,
路由跳转的核心方法是transitionTo,在跳转过程中经历了一次confirmTransition,
(beforeRouteLeave
在第一个queue迭代完毕后,执行第二个(beforeRouteEnter
在执行完毕后,开始执行updateRoute,之后执行全局的afterEach守卫。最后完成路由的跳转。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!