🤖

Vue RouterとTransitionで画面遷移の実装したときにハマった

2023/10/27に公開

はじめに

Vue RouterとVueのビルトインコンポーネントであるTransitionで画面遷移時のアニメーションを適用することにした。
しかし、一部のコンポーネントの読み込みが以上に遅くなってしまったので、調査〜解決まで行ってみた。

TL;DR

<Transition>は子要素として単一ノードでなければならない。
<component><div>で囲って一つの要素にする必要があった。
ただ、<Transition>のアニメーションを発火させるため、<div>key属性を追加する必要がある。
https://stackoverflow.com/a/70042452

Transitions require single children nodes. Therefore you can wrap the <component> tag inside a <div>, however, a plain <div> inside a <transition> won't trigger the transition, but changing the key attribute does.

開発環境

  • PC
    • MacBook Pro
    • macOS(Sonoma)
  • フレームワーク・ライブラリ
    • Vue
      • 3.2.37
    • Vue Router
      • 4.1.6
    • Vite
      • 3.1.0
    • TailwindCSS
      • 3.2.1

ソース

BEFORE

App.vue
<template>
  <div class="h-screen">
      <RouterView
        name="Header"
        v-slot="{ Component }"
      >
        <Transition
          mode="out-in"
          enter-active-class="transition-opacity"
          leave-active-class="transition-opacity"
          enter-from-class="opacity-0"
          leave-to-class="opacity-0"
        >
          <component :is="Component" />
        </Transition>
      </RouterView>
      <RouterView v-slot="{ Component }">
        <Transition
          mode="out-in"
          enter-active-class="transition-opacity"
          leave-active-class="transition-opacity"
          enter-from-class="opacity-0"
          leave-to-class="opacity-0"
        >
          <component :is="Component" />
        </Transition>
      </RouterView>
  </div>
</template>

<script setup lang="ts">
  import { RouterView } from 'vue-router'
</script>

AFTER

App.vue
      <RouterView 
        name="Header"
-       v-slot="{ Component }"
+       v-slot="{ Component, route }"
      >
        <Transition
          mode="out-in"
          enter-active-class="transition-opacity"
          leave-active-class="transition-opacity"
          enter-from-class="opacity-0"
          leave-to-class="opacity-0"
        >
+         <div :key="route.fullPath">
+            <component :is="Component" />
+         </div>
        </Transition>
      </RouterView>

やったこと

  • <RouterView>に渡しているv-slotrouteを追加
    • 後述する囲い<div>key属性に渡すユニークな値としてroute.fullPathを使用
    • ちなみに<RouterView>v-slotを渡していることに関してはこちら
  • <component><div>で囲い、<Transition>内で単一の子要素を作る

結果

BEFORE

結果のBEFORE画像
遷移時にもヘッダーが常に表示され、メインコンテンツの表示に遅延が発生している

AFTER

結果のAFTER画像

まとめ&感想

最近まで画面遷移時のアニメーションなど今まで実装してなかったり、考えてもこなかったんですが、Vueの<Transition>を使ってみてUXが大分良くなったなぁって感じました。
あと、今回のバグ直す時に他の人は自分と違って名前付きビューで実装してなかったので、地味に時間かかりました。。
もし、間違ってたりもっと良い実装方法があれば教えてください!

GitHubで編集を提案

Discussion