Vue.js パフォーマンス最適化完全ガイド

その他

はじめに

Vue.js を使ったアプリ開発では、パフォーマンスの最適化が重要です。アプリが 重くなる主な原因 としては、次のようなものがあります。

  • 不要なリレンダリング(無駄な状態更新)
  • 無駄なデータ取得(API の過剰な呼び出し)
  • 大きなバンドルサイズ(初回ロードの遅延)

パフォーマンスを最適化することで、UX 向上・ページ速度改善・メモリ使用量の削減 につながります。

Vue の高速化テクニック

Vue では、次の方法でパフォーマンスを向上させることができます。

computed() を活用して無駄な再計算を防ぐ

リアクティブなデータを直接テンプレート内で計算すると、毎回レンダリングされるたびに計算されてしまう ため、パフォーマンスが低下します。

computed() を使うことで、依存するデータが変わったときのみ計算が実行され、結果がキャッシュ されます。

<script setup>
import { ref, computed } from 'vue';

const price = ref(1000);
const taxRate = ref(0.1);

// 価格が変更されたときのみ再計算
const totalPrice = computed(() => price.value * (1 + taxRate.value));
</script>

v-if と v-show の使い分け

Vue では、要素を表示/非表示にするために v-ifv-show を使いますが、それぞれの動作が異なります。

v-ifv-show
仕組みDOM に要素を追加/削除CSS で display: none
適しているケース条件によって要素を完全に消す場合頻繁に表示/非表示を切り替える場合
レンダリングコスト高い(要素を追加/削除)低い(CSS のみ変更)
<template>
  <p v-if="isVisible">v-if は DOM の追加削除を行う</p>
  <p v-show="isVisible">v-show は CSS の display: none を適用する</p>
</template>

イベントのデバウンス処理

検索機能や入力フォームで、ユーザーが キーを押すたびに API を呼び出す と無駄なリクエストが増えます。
これを防ぐために「デバウンス」を活用します。

<script setup>
import { ref } from 'vue';

const keyword = ref('');
let timer = null;

const search = () => {
  clearTimeout(timer);
  timer = setTimeout(() => {
    console.log(`検索実行: ${keyword.value}`);
  }, 500); // 0.5秒ごとに実行
};
</script>

<template>
  <input v-model="keyword" @input="search" placeholder="検索..." />
</template>

ユーザーが入力を終えて 0.5 秒経過するまで検索を実行しません!

keep-alive で不要なリレンダリングを防ぐ

Vue の keep-alive を使うと、コンポーネントの 状態をキャッシュ し、不要なリレンダリングを防ぐことができます。
特に Vue Router でページを切り替えるときタブ切り替えのような UI で有効です。

keep-alive の基本的な使い方

通常、<router-view /> の中のコンポーネントは ページを切り替えるたびに破棄され、再作成されます
しかし、<keep-alive> で囲むと、一度表示したコンポーネントはメモリに保持され、再描画されなくなります

<template>
  <keep-alive>
    <router-view />
  </keep-alive>
</template>

ページを切り替えても、コンポーネントの状態が保持される!
無駄なリレンダリングがなくなり、パフォーマンスが向上!

keep-alive が効果的な場面

  • タブ切り替えのような UI
  • 検索結果などのキャッシュを残したい場合
  • リスト画面と詳細画面を行き来するとき
  • フォーム入力を途中で保存し、戻っても消えないようにする

たとえば、以下のようにタブ切り替えを keep-alive で最適化できます。

<template>
  <div>
    <button @click="currentTab = 'tab1'">タブ1</button>
    <button @click="currentTab = 'tab2'">タブ2</button>

    <keep-alive>
      <component :is="currentTabComponent" />
    </keep-alive>
  </div>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue';

const currentTab = ref('tab1');
const currentTabComponent = computed(() =>
  currentTab.value === 'tab1' ? Tab1 : Tab2
);

const Tab1 = defineAsyncComponent(() => import('./Tab1.vue'));
const Tab2 = defineAsyncComponent(() => import('./Tab2.vue'));
</script>
  • keep-alive によって、タブの内容がキャッシュされるため、不要な再描画を防げる!

keep-alive を使うと mounted() は最初の 1 回しか実行されない

通常、コンポーネントは 表示されるたびに mounted() が実行 されますが、keep-alive を適用すると 最初の 1 回しか実行されなくなります
つまり、コンポーネントの表示・非表示に合わせて何か処理をしたい場合は、activated() / deactivated() フックを使う必要があります

activated() / deactivated() の使い方

  • onActivated()keep-alive されたコンポーネントが再表示されるときに実行される
  • onDeactivated()keep-alive されたコンポーネントが非表示になるときに実行される

タブを切り替えたときにログを出力

<script setup>
import { onActivated, onDeactivated } from 'vue';

onActivated(() => {
  console.log('🔵 コンポーネントが表示されました');
});

onDeactivated(() => {
  console.log('🔴 コンポーネントが非表示になりました');
});
</script>
  • onActivated() は、ページを戻ってきたときに実行される
  • onDeactivated() は、ページを離れるときに実行される

たとえば、ページを切り替えたときに API を再取得する ような処理も可能です。

API データをキャッシュしつつ、タブを切り替えたら再取得する

<script setup>
import { ref, onMounted, onActivated } from 'vue';
import axios from 'axios';

const data = ref(null);

const fetchData = async () => {
  const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
  data.value = response.data;
};

// 初回のみ API を取得
onMounted(fetchData);

// ページを戻ってきたときに API を再取得
onActivated(fetchData);
</script>

<template>
  <div>
    <p v-if="data">{{ data.title }}</p>
  </div>
</template>
  • 初回の onMounted() で API を取得
  • タブを戻ってきたときに onActivated() でデータを更新
  • keep-alive によって、タブを切り替えてもデータが残る!

v-memo や Lazy Loading の活用

Vue 3 では、パフォーマンスを最適化するための新機能として v-memo が導入され、また Lazy Loading(遅延読み込み) を活用することで、アプリの初回ロードを高速化できます。

v-memo(Vue 3 の新機能)

Vue 3 の v-memo ディレクティブを使うと、特定の値が変わらない限り要素を再レンダリングしない ようにできます。
これにより、不要な再描画を減らし、パフォーマンスを向上 させることが可能です。

v-memo の基本的な使い方
<template>
  <div v-memo="[count]">
    <p>{{ count }}</p>
  </div>
</template>
  • count の値が変わらない限り、この <div> 内の要素は 再レンダリングされません!
v-memo の動作を確認する
<script setup>
import { ref } from 'vue';

const count = ref(0);
const otherValue = ref(0);
</script>

<template>
  <div v-memo="[count]">
    <p>カウント: {{ count }}</p>
  </div>

  <button @click="count++">count を増やす</button>
  <button @click="otherValue++">otherValue を増やす</button>
</template>

otherValue を増やしても、v-memo="[count]" を設定している部分は再描画されません!
count の値が変わったときのみ、この要素が更新されます。

v-memo を使うメリット
  • リスト要素の再描画を防ぐ(特に大規模リストで有効)
  • 複雑な UI の再レンダリングを抑えてパフォーマンスを向上
  • 無駄な DOM 操作を減らして、CPU 処理の負荷を軽減

Lazy Loading(遅延読み込み)

Lazy Loading(遅延読み込み)とは、必要なときにのみコンテンツをロードする 手法です。
これにより、ページの初回ロードを高速化し、不要なリソースの読み込みを抑える ことができます。

画像の遅延読み込み(Lazy Loading)

画像はサイズが大きく、特に多くの画像を含むページでは すべての画像を最初に読み込むとパフォーマンスが低下 します。
そのため、スクロール時に 必要な画像だけを遅延ロードする(Lazy Load) のが一般的です。

通常の画像読み込み

<img src="image.jpg" alt="通常の画像" />
  • ページがロードされるとすぐに image.jpg をダウンロードする
  • すべての画像を一度に読み込むため、ページが重くなる

遅延読み込み(Lazy Loading)

v-lazy などのライブラリを使って スクロール時に画像を読み込む ことができます。

<img v-lazy="imageUrl" />
  • スクロールしたときに初めて画像が読み込まれる!
  • 不要な画像を最初に読み込まないため、ページの読み込み速度が向上!

Vue 公式では v-lazy は提供されていませんが、vue-lazyload などのライブラリを使うことで実装可能です。

コンポーネントの遅延読み込み

Vue では、コンポーネントを 動的に遅延ロード(Lazy Load) することができます。
これにより、初回ロード時にすべてのコンポーネントを読み込むのではなく、必要なときにだけロードする ことができます。

defineAsyncComponent() を使ったコンポーネントの遅延読み込み
<script setup>
import { defineAsyncComponent } from 'vue';

const LazyComponent = defineAsyncComponent(() => import('./MyComponent.vue'));
</script>

<template>
  <LazyComponent />
</template>
  • 初回ロード時に MyComponent.vue をダウンロードしない
  • LazyComponent が初めて表示されたタイミングでロードされる
  • バンドルサイズを削減し、ページ表示を高速化できる
遅延読み込み時にローディング表示を追加

defineAsyncComponent()loadingComponent を指定することで、ロード中に表示するコンポーネントを設定 できます。

<script setup>
import { defineAsyncComponent } from 'vue';

// 遅延ロードするコンポーネント
const LazyComponent = defineAsyncComponent({
  loader: () => import('./MyComponent.vue'),
  loadingComponent: () => import('./LoadingSpinner.vue'),
  delay: 200, // 200ms 待ってからローディング表示
});
</script>

<template>
  <LazyComponent />
</template>
  • コンポーネントが読み込まれるまで「ローディングスピナー」を表示
  • ユーザーに「読み込み中」であることを伝えられるので UX 向上

Chrome DevTools を使ったパフォーマンス測定

  1. F12キー(開発者ツール)を開く
  2. Performance タブで「Record」をクリック
  3. アプリを操作してパフォーマンスを計測
  4. 不要なリレンダリングが発生していないか確認

まとめ

  • computed() を使い、無駄な計算を防ぐ
  • v-ifv-show を適切に使い分ける
  • watch() の代わりに debounce() を活用し、不要なリクエストを削減
  • keep-alive を使ってコンポーネントの再レンダリングを防ぐ
  • v-memo を活用し、不要な描画を減らす
  • コード分割(遅延読み込み)を行い、バンドルサイズを削減

これらのテクニックを活用することで、Vue.js アプリのパフォーマンスを大幅に向上させることができます!

コメント

タイトルとURLをコピーしました