Skip to content

20260707tue

Published: at 15:00

Nuxt 4 ライフサイクルフック完全ガイド

before 系フック(beforeCreate / onBeforeMount / onBeforeUpdate / onBeforeUnmount 等)は除外しています。

Nuxt 4 のコンポーネントは SSR(サーバー)CSR(クライアント) の 2 つのフェーズで動作します。
各フックがどのタイミング・どのコンテキストで実行されるかを理解することが、パフォーマンスとデータフェッチ最適化の鍵です。


実行コンテキスト凡例

マーク説明
🟢 クライアントブラウザのみで実行
🟡 サーバーSSR 時のみ実行
🔵 両方SSR・CSR どちらでも実行

実行フロー

SERVER SIDE (SSR)                CLIENT SIDE (CSR / Hydration)
─────────────────────────        ─────────────────────────────
app:created         🔵           app:created         🔵
     ↓                                ↓
  setup()           🔵            setup()            🔵
     ↓                                ↓
app:rendered        🟡           onMounted()         🟢

                                 app:mounted         🟢

                                page:start           🟢

                                page:finish          🟢

                               onUpdated()  ← リアクティブ変更

                               onUnmounted()

Vue 3 ライフサイクルフック(Composition API)

setup() 🔵 両方

コンポーネントライフサイクルの起点。Options API の beforeCreate / created に相当し、SSR・CSR 両方で実行されます。
refreactivecomputed の定義や、他のフックの登録はここで行います。DOM にはまだアクセスできません。

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

// setup は SSR・CSR 両方で実行される
const count = ref(0);
const double = computed(() => count.value * 2);
</script>

⚠️ SSR でも実行されるため、windowdocument へのアクセスはここでは行わないでください。


onMounted() 🟢 クライアント

コンポーネントの DOM ツリーが作成され、ブラウザへ挿入された後に呼ばれます。
DOM 操作・外部ライブラリの初期化・ブラウザ API へのアクセス に最適なフックです。

<script setup>
import { onMounted } from "vue";

const chartEl = useTemplateRef("chart");

onMounted(() => {
  // DOM が確実に存在するため安全に操作できる
  initChart(chartEl.value);
});
</script>

<template>
  <canvas ref="chart" />
</template>

onUpdated() 🟢 クライアント

リアクティブな状態が変化し、DOM が更新された直後に呼ばれます。更新後の DOM 状態を読み取る必要がある場合に使用します。

<script setup>
import { onUpdated } from "vue";

const listEl = useTemplateRef("list");

onUpdated(() => {
  // 追加後のリストにスクロールする例
  listEl.value.scrollTo({ top: listEl.value.scrollHeight, behavior: "smooth" });
});
</script>

⚠️ このフック内でリアクティブ状態を変更すると無限ループになります。条件チェックが必要な場合は watchEffect を検討してください。


onUnmounted() 🟢 クライアント

コンポーネントが DOM から取り外された後に呼ばれます。
タイマー・イベントリスナー・外部サブスクリプション など、コンポーネント外に影響するリソースのクリーンアップに使用します。

// app/composables/useResize.ts
import { onMounted, onUnmounted } from "vue";

export function useResize(callback: () => void) {
  onMounted(() => {
    window.addEventListener("resize", callback);
  });

  onUnmounted(() => {
    // リスナーを確実に解除してメモリリークを防ぐ
    window.removeEventListener("resize", callback);
  });
}

onActivated() / onDeactivated() 🟢 クライアント

<KeepAlive> でラップされたコンポーネント専用のフックです。

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

onActivated(() => {
  // タブに戻ったタイミングでデータを再フェッチ
  refreshData();
});

onDeactivated(() => {
  // バックグラウンドになったとき処理を一時停止
  pausePolling();
});
</script>

onErrorCaptured() 🔵 両方

子孫コンポーネントから伝播したエラーをキャッチします。false を返すとエラーの伝播を止めます。エラーバウンダリの実装に使用します。

<script setup>
import { ref, onErrorCaptured } from "vue";

const error = (ref < Error) | (null > null);

onErrorCaptured(err => {
  error.value = err;
  return false; // エラーの伝播を止める
});
</script>

<template>
  <div v-if="error">エラー: {{ error.message }}</div>
  <slot v-else />
</template>

Nuxt アプリフック(useNuxtApp().hook()

Nuxt 固有のフックは useNuxtApp().hook() で登録します。プラグイン・ミドルウェア・コンポーザブル内から呼び出せます。


app:created 🔵 両方

Nuxt が Vue アプリインスタンスを作成した直後に発火します。Vue プラグインの追加やグローバル設定など、アプリ全体に影響する初期化処理に使用します。

// app/plugins/global.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("app:created", vueApp => {
    // Vue アプリへグローバルプロパティを追加
    vueApp.config.globalProperties.$format = formatCurrency;
  });
});

app:mounted 🟢 クライアント

Vue アプリが DOM へマウントされた後(Hydration 完了後)に発火します。アプリ全体で一度だけ必要な初期化処理に最適です。

// app/plugins/analytics.client.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("app:mounted", () => {
    // ブラウザのみで動作する分析ツールを初期化
    initAnalytics({ id: "GA-XXXXXXXX" });
  });
});

💡 プラグインファイル名に .client.ts を付けると、そのプラグイン自体がクライアントサイドのみで実行されます。


app:rendered 🟡 サーバー

サーバーサイドレンダリングが完了し、HTML が確定した後に発火します。レスポンスを送出する前の最終処理に使用します。

// app/plugins/ssr-only.server.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("app:rendered", renderContext => {
    renderContext.ssrContext!.event.node.res.setHeader(
      "X-Rendered-At",
      new Date().toISOString()
    );
  });
});

app:error / app:error:cleared 🔵 両方

// app/plugins/error-reporting.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("app:error", error => {
    // Sentry などの外部サービスへ報告
    reportToSentry(error);
  });

  nuxtApp.hook("app:error:cleared", ({ redirect }) => {
    redirect("/");
  });
});

app:suspense:resolve 🟢 クライアント

すべての非同期データが解決され、ページが表示可能な状態になったタイミングで発火します。ページ遷移のローディングインジケーター制御に適しています。

// app/plugins/loading.client.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("app:suspense:resolve", () => {
    useLoadingState().value = false;
  });
});

ページ遷移フック 🟢 クライアント

<NuxtPage> コンポーネントによるページ遷移のライフサイクルを制御します。すべてクライアントサイドのみで実行されます。


page:start / page:finish

// app/plugins/page-loading.client.ts
export default defineNuxtPlugin(nuxtApp => {
  const loading = useLoadingIndicator();

  nuxtApp.hook("page:start", () => {
    loading.start();
  });

  nuxtApp.hook("page:finish", () => {
    loading.finish();
  });
});

💡 Nuxt には組み込みの useLoadingIndicator() コンポーザブルがあります。


page:transition:finish

Vue の <Transition> によるページ遷移アニメーションが完了した後に発火します(onAfterLeave イベント後)。

// app/plugins/scroll.client.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook("page:transition:finish", () => {
    // アニメーション完了後にページ先頭へスクロール
    window.scrollTo({ top: 0, behavior: "instant" });
  });
});

フック一覧まとめ

フックコンテキスト主な用途
setup()🔵 両方リアクティブ定義・フック登録
onMounted()🟢 クライアントDOM 操作・ライブラリ初期化
onUpdated()🟢 クライアント更新後の DOM 参照
onUnmounted()🟢 クライアントリソースのクリーンアップ
onActivated()🟢 クライアントKeepAlive 復元時
onDeactivated()🟢 クライアントKeepAlive キャッシュ時
onErrorCaptured()🔵 両方エラーバウンダリ
app:created🔵 両方アプリ全体の初期化
app:mounted🟢 クライアントHydration 完了後の処理
app:rendered🟡 サーバーSSR レスポンス送出前の処理
app:error🔵 両方エラー報告
app:error:cleared🔵 両方エラー解消後の処理
app:suspense:resolve🟢 クライアント非同期解決後の処理
page:start🟢 クライアントページ遷移開始
page:finish🟢 クライアントページ遷移完了
page:transition:finish🟢 クライアントアニメーション完了後