vue-routerにはonReadyというフックが用意されている。
ドキュメントを読めばわかるように、これは when the router has completed the initial navigation, which means it has resolved all async enter hooks and async components that are associated with the initial route. 、つまり最初に表示するルートに紐付けられたbeforeEnter ナビゲーションガードやbeforeEachglobalナビゲーションガードが解決され、「どのrouteが実行されるべきか」が解決されたあとに呼ばれる。
これが便利になるのはSSRのときで、preloadしたいデータがあるような場合はbeforeEnter内で非同期読み込みし、その読み込みが終わったらnextするようにしておき、onReadyのタイミングでSSRすればよい、というような使いかたができるわけだ。
ところで、ナビゲーションガードが解決されたあとに呼ばれるhookはもうひとつある。それがafterEachグローバルhookだ。
このafterEachというhookとonReadyhookはどちらが先に呼び出されるのだろう。ドキュメントを読んでもよくわからなかったので、ソースを追った。
まず、onReadyで登録したhookを実際に呼び出しているのはここである。
transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) { const route = this.router.match(location, this.current) 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) }) } }) }
で、その前にコールされてるthis.updateRoute(route)の中身見ると
updateRoute (route: Route) { const prev = this.current this.current = route this.cb && this.cb(route) this.router.afterHooks.forEach(hook => { hook && hook(route, prev) }) }
ここでafterHooksを呼んでいる。というわけで、onReadyよりも先にafterEatchのhookが呼ばれるようになっていることが確認できた。
注意すべき点として、(あたりまえだが)afterEachは同期的に呼び出されているので、その中で非同期な操作を行った場合、その非同期操作の完了を待たずにonReadyhookが呼び出されるので、onReadyにhookしてSSRしても、afterEach内で行った非同期操作の結果はSSRされない。