記事検索

検索ワードを入力してください。
Sky Tech Blog
【Vue】defineAsyncComponent - 非同期コンポーネントに​ついて

【Vue】defineAsyncComponent - 非同期コンポーネントに​ついて

ここでは、Vue.jsの非同期コンポーネントであるdefineAsyncComponentの基本的な使い方とその利点について、具体的なサンプルコードを交えて解説しています。パフォーマンス向上のための実装方法やエラー処理についても説明しています。

はじめに

Vue.jsの勉強をしていて、defineAsyncComponentというものに出会ったので、学んだことをまとめました。

非同期コンポーネントとは

非同期コンポーネントとは、コンポーネントを必要なときに動的にロードする機能です。
画面に表示されていないときなどの不要なときにコンポーネントの読み込みが発生しないため、初期ロード時などの場面ではパフォーマンスの向上が期待できます。

VueではdefineAsyncComponent関数を使用して、非同期コンポーネントを実装します。

defineAsyncComponentの​基本的な​使い方

例としてWebサイトでよく目にする、Step1からStep2へ遷移するような場面で挙動を確認していきます。
また本記事のサンプルコードは、スタイル指定にSCSS記法を採用しています。(SCSS記法を使用するには別途設定が必要です。)

まずは非同期コンポーネントを使用しない場合です。
ボタンがクリックされるとSTEPが切り替わるようにしています。

defineAsyncComponentを​使用しない​場合

【サンプルコード】

▼App.vue

<template>
  <div :class="$style.wrapper">
    <button :class="$style.button" @click="isShow = !isShow">
      STEP{{ isShow ? "1" : "2" }}へ
    </button>

    <Step1 v-if="!isShow" />
    <Step2 v-if="isShow" />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Step1 from "./components/Step1.vue";
import Step2 from "./components/Step2.vue";

const isShow = ref(false);
</script>

<style lang="scss" module>
.wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding-block: 30px;
}
.button {
  width: 200px;
  height: 40px;
  font-size: x-large;
}
</style>

▼Step1.vue

<template>
  <div :class="$style.wrapper">
    <h1 :class="$style.title">STEP1</h1>
  </div>
</template>

<script setup lang="ts"></script>

<style lang="scss" module>
.wrapper {
  display: flex;
  align-items: center;
  width: 80%;
  height: 500px;
  margin: auto;
  background-color: skyblue;
  .title {
    flex: 1;
    font-size: 5rem;
    text-align: center;
  }
}
</style>

▼Step2.vue

<template>
  <div :class="$style.wrapper">
    <h1 :class="$style.title">STEP2</h1>
  </div>
</template>

<script setup lang="ts"></script>

<style lang="scss" module>
.wrapper {
  display: flex;
  align-items: center;
  width: 80%;
  height: 500px;
  margin: auto;
  background-color: yellowgreen;
  .title {
    flex: 1;
    font-size: 5rem;
    text-align: center;
  }
}
</style>

【結果​】

初期表示時にSTEP2は表示されないのですが、ブラウザの検証ツールのネットワークタブを確認すると、STEP2のコンポーネントも読み込まれていることがわかります。
サンプルのように2つのステップしかなく、中身が文字だけであればパフォーマンスへの影響はほとんどありませんが、ステップ数や中身が多くて複雑になればパフォーマンスに影響が出てくるでしょう。

次にdefineAsyncComponentを使用した場合を見てみます。

Step2コンポーネントを初期ロード時に読み込まないようにします。

以下がdefineAsyncComponentの最もシンプルな使用方法ですので、こちらを利用して実装してみます。

const AsyncComponent = defineAsyncComponent(
  () => import("./components/Step2.vue")
);

実際に先ほどのサンプルコードに落とし込んでみます。

defineAsyncComponentを​使用した​場合

【サンプルコード】

▼App.vue

<template>
  <div :class="$style.wrapper">
    <button :class="$style.button" @click="isShow = !isShow">
      STEP{{ isShow ? "1" : "2" }}へ
    </button>

    <!-- この部分 -->
    <Step1 v-if="!isShow" />
    <AsyncComponent v-if="isShow" />
  </div>
</template>

<script setup lang="ts">
import { ref, defineAsyncComponent } from "vue";
import Step1 from "./components/Step1.vue";

const isShow = ref(false);

// この部分
const AsyncComponent = defineAsyncComponent(
  () =>
    import("./components/Step2.vue")
);
</script>

<style lang="scss" module>
// 省略
</style>

【結果​】

先ほどと同じようにネットワークを確認してみると、初期ロード時にStep2コンポーネントが読み込まれていないことがわかります。

今回はボタンをクリックして初めてStep2コンポーネントが読み込まれます。

※補足

以下のようにしてpropsやslotを渡すことも可能です。

propsと​して​titleを​渡す場合
<AsyncComponent v-if="isShow" title="Step2のタイトル" />
slotを​渡す場合
<AsyncComponent v-if="isShow" title="Step2のタイトル">
  <div>コンテンツ</div>
</AsyncComponent>

ローディングと​エラーの​状態を​設定する

少し応用的な使い方になるのですが、非同期コンポーネントの読み込み中や、読み込みに失敗した際に表示するコンポーネントを設定することもできます。
今回はボタンをクリックするとモーダルが表示される場面で挙動を確認していきます。

defineAsyncComponentを​使用しない​場合

【サンプルコード】

▼App.vue

<template>
  <div :class="$style.wrapper">
    <button :class="$style.button" @click="isShow = !isShow">ブログ詳細</button>
    <template v-if="isShow">
      <BlogModal title="非同期コンポーネント" />
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import BlogModal from "./components/BlogModal.vue";

const isShow = ref(false);
</script>

<style lang="scss" module>
.wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding-block: 30px;

  .button {
    width: 200px;
    height: 40px;
    font-size: x-large;
  }
}
</style>

▼BlockModal.vue

<template>
  <div :class="$style.wrapper">
    <div :class="$style.blog">
      <h1 :class="$style.title">{{ props.title }}</h1>
      <img :class="$style.thumbnail" src="./site-logo_sky.svg" />
      <div :class="$style.body">
        <p>ここからがブログの本文です。</p>
        <p v-for="n in 10" :key="n">
          {{
            n
          }}行目。ブログの本文です。ブログの本文です。ブログの本文です。ブログの本文です。
        </p>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
interface Props {
  title: string;
}
const props = withDefaults(defineProps<Props>(), {
  title: "タイトルです。",
});
</script>

<style lang="scss" module>
.wrapper {
  display: flex;
  align-items: center;
  justify-content: center;

  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.6);

  .blog {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 80%;
    padding: 10px;
    gap: 10px;
    background-color: white;

    .title {
      width: 100%;
      text-align: center;
      font-size: 2rem;
      font-weight: 800;
      background-color: aliceblue;
    }

    .thumbnail {
      width: 100%;
    }

    .body {
      width: 100%;
      padding: 10px;
      background-color: aliceblue;
      box-sizing: border-box;
    }
  }
}
</style>

【結果​】

先ほどのStep遷移の例と同じように、defineAsyncComponentを使用しない場合は、表示されていないコンポーネントも読み込まれていることがわかります。

defineAsyncComponentを​使用した​場合

【サンプルコード】

▼App.vue

<template>
  <div :class="$style.wrapper">
    <button :class="$style.button" @click="isShow = !isShow">ブログ詳細</button>
    <template v-if="isShow">
        <!-- この部分 -->
      <AsyncBlogModal title="非同期コンポーネント" />
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref, defineAsyncComponent } from "vue";
import Loading from "./components/Loading.vue";
import Error from "./components/Error.vue";

const isShow = ref(false);

// この部分
const AsyncBlogModal = defineAsyncComponent({
  loader: () => import("./components/BlogModal.vue"),
  loadingComponent: Loading,
  errorComponent: Error,
  delay: 2000,
  timeout: 5000,
});
</script>

<style lang="scss" module>
// 省略
</style>

【結果​】

初期ロード時にモーダル用のコンポーネントが読み込まれていないことがわかります。

ボタンをクリックして初めてモーダル用のコンポーネントが読み込まれています。

今回のサンプルコードは、Step遷移の時とは違い少し行数が多いため、各行の説明をさせていただきます。

ローダー関数

非同期コンポーネントを読み込むための関数

loader: () => import("./components/BlogModal.vue")

以下のように処理を追加することも可能です。

loader: () => {
  console.log("処理を追加できる");
  import("./components/BlogModal.vue")
}
ローディングコンポーネント

非同期コンポーネントの読み込み中に表示するコンポーネント

loadingComponent: Loading

遅延

ローディングコンポーネントを表示する前の遅延時間(ミリ秒)

delay: 2000
エラーコンポーネント

読み込みに失敗した場合に表示するコンポーネント

errorComponent: Error

タイムアウト

エラーコンポーネントを表示するまでの時間(ミリ秒)

timeout: 5000

エラーコンポーネントの表示を確認するために、delayに設定した値よりも短い時間をtimeoutに設定して意図的にエラーを発生させてキャプチャを取っています。

参考資料

公式ドキュメント


\シェアをお願いします!/
  • X
  • Facebook
  • LINE
キャリア採用募集中!

入社後にスキルアップを目指す若手の方も、ご自身の経験を幅広いフィールドで生かしたいベテランの方も、お一人おひとりの経験に応じたキャリア採用を行っています。

Sky株式会社のソフトウェア開発や製品、採用に関するお問い合わせについては、下記のリンクをご確認ください。
お問い合わせ
ホーム