beta

Vue.jsで、入れ子になったコンポーネントの読み込みの動きを確認してみた

Vue.jsでSPAを開発している時に、変数のやり取りや挙動などの制御できないことがあったので、入れ子になったコンポーネントの遷移ごとのライフサイクルを確認してみました。

公開日:2019年8月21日

公式のライフサイクル図

Vue.js公式ページにあります。

Vueコンポーネントのライフサイクルダイアグラム

ライフサイクルダイアグラム | Vue.js

Vueが呼ばれてインスタンスを作成してから削除されるまでの一覧です。

わかりやすいのですが、コンポーネントが入れ子になっている際の動きがわからなかったので、確認してみました。

コンポーネントの構成とテスト

App.vue
  -- Parent.vue
     --- Child.vue
        -- parts.vue
  -- Parent2.vue        

それぞれのcreated、mouted、watchにconsole.logでメッセージを表示させる方法で確認しました。

各ファイルでは

created () {
  console.log('XXX created');
},
mounted () {
  console.log('XXX mounted');
watch: {
  '$route' (to, from) {
    console.log('XXX route change watch');
  }
},

という形で、メッセージを出すようにしました。

遷移ごとの挙動の違い

最初の立ち上げ(イニシャルロード)

まず、ページを初めてロードした時の動きです。ページリフレッシュした場合も同じ動きになります。

結果は下記のようになりました。

App.vue?234e:53 App created
Parent.vue?9924:70 Parent created
Child.vue?e9a9:65 Child created
parts.vue?93f8:10 parts create
parts.vue?93f8:13 parts mount
Child.vue?e9a9:68 Child mounted
Parent.vue?9924:75 Parent mounted
App.vue?234e:53 App mounted

まずroot直下のAppインスタンスがcreateされています。

そのあとは、Appインスタンスがmountedするのかと思っていたら、App.vueで内包している(router-viewで表示)Parent.vueがcreatedされました。

Parent.vueがcreatedされたのなら、そのままParent.vueをmountedまでするのかと思いきや、今度はChild.vueをcreatedします。

ここまで来ると推測できますが、このあとは、Child.vueはmountedしないで、parts.vueをcreated、この先は内包しているコンポーネントがないため、parts.vueをmountedして、あとは遡ってChild.vue、Parent.vue、App.vueの順番でmoutedしていきました。

簡単に言えば、内包するインスタンスをcreated & moutendしてから、自分自身をmoutedするということですね。

別ルートへのページ遷移の場合

次に、Parent.vueからParent2.vueへのルート変更の場合です。

結果は以下のようになりました。

App watch RouteChange
router.js?41cb:28 null
Parent2.vue?a2a1:181 Parent2 created
Parent2.vue?a2a1:184 Parent2 mounted

大元のApp.vueだけでwatchが働いて、あとはParent2をcreated、moutendしています。

Parent.vue側に「beforeRouteUpdate」を入れてみても、そちらも反応はしていませんでした。

Keep-aliveしているコンポーネントからルート変更する場合

続いて、同じくParent.vueからParent2へルートチェンジですが、今度はParent.vue以下をkeep-aliveしている場合です。

App.vue?234e:73 App watch RouteChange
Parent.vue?9924:82 Parent route change watch
Child.vue?e9a9:105 Child route change watch
parts.vue?93f8:18 parts route change watch
router.js?41cb:28 null
Parent2.vue?a2a1:181 Parent2 created
Parent2.vue?a2a1:184 Parent2 mounted

Appからpartsまで順々にwatchしてから、Parent2がcreated、moutedされています。

一度作成したインスタンスは、keep-aliveでインスタンスをキープしておいても、createdもmountedもされませんが、watchが反応するので、ここで処理するのが良さそうです。

同一ルートでのURL変更の場合はどうか

次に、同一ルートでURLが変更された場合です。「/posts/1」から「/post/2」へのページ遷移などがこれにあたります。

App watch RouteChange
Parent.vue?9924:82 Parent route change watch
Child.vue?e9a9:104 Child route change watch
parts.vue?93f8:18 parts route change watch
router.js?41cb:28 null

Appから順番にpartsまでwatchされていることがわかります。動きとしてはkeep-aliveと、同一インスタンス内でのルート変更が同じのようです。

戻るボタン(history back)で戻ってきた場合

次に、Parent2に遷移した後に、ブラウザの戻るボタン(もしくはrouter.go(-1))でParentに戻ってきた時の挙動です。

App watch RouteChange
Parent.vue?9924:82 Parent route change watch
Child.vue?e9a9:105 Child route change watch
parts.vue?93f8:18 parts route change watch
router.js?41cb:28 {x: 0, y: 1025}

Parentに関連するすべてのwatchが反応していますね。

scrollBehaviorの動き

先ほどからのログに

router.js?41cb:28 null

というのがありますが、これは実は、router.jsで

Vue.use(Router)

const router = new Router({
  mode: 'history',
  scrollBehavior (to, from, savedPosition) {
    console.log(savedPosition);
  }
}

としている部分の表示でした。

ここまでのログを見ていると、

  • 最初の立ち上げ(イニシャルロード)-> 反応しない
  • 別ルートへのページ遷移の場合 -> null
  • Keep-aliveしている場合のルート変更の場合 -> null
  • 同一ルートでのURL変更の場合はどうか -> null

となりました。これは当たり前で、scrollBehaviorは、ページ遷移でもブラウザの戻る・進むボタンの遷移でしかpositionを返さない仕様だからです。

スクロールの振る舞い | Vue Router

なので、「戻るボタン(history back)で戻ってきた場」合のみ、x、yのポジションが帰ってきています。

ここも考慮に入れてくべきでしょう。


Vue.jsで入れ子になったコンポーネントのライフサイクルをみてきました。

理路整然としていて、理解してしまえば納得なのですが、しっかり抑えておくポイントもあるので、忘れないようにしないとですね。