Vue.js

Nuxt.jsのnuxtServerInitアクションの使い方

Nuxt.jsでVuexを利用しているとき、fetch()によってコンポーネントがロードされるたびにサーバーサイドレンダリングでデータを取得したくない場合があります。 最初の一度だけサーバーサイドでレンダリングしたいときに、Nuxt.jsの特別なアクションであるnuxtServerInitを使います。

この記事の目次
  1. Nuxt.jsのnuxtServerInitアクションの使い方
  2. async/awaitでAPIから取得する例
  3. 動かないときの確認
  4. Nuxt 3にはnuxtServerInitがない

Nuxt.jsでVuexを利用しているとき、fetch()によってコンポーネントがロードされるたびにサーバーサイドレンダリングでデータを取得したくない場合があります。
最初の一度だけサーバーサイドでレンダリングしたいときに、Nuxt.jsの特別なアクションであるnuxtServerInitを使います。

結論から言うと、nuxtServerInitはNuxt 2 + Vuexで使える特別なアクションで、store/index.jsactionsに書くと、サーバー側でページを描画する前に一度だけ自動で呼ばれます。第一引数はVuexのcontext(commitなどを持つ)、第二引数はNuxtのcontext(reqparamsappなど)です。Nuxt 3にはこのアクションが存在しないので、後述のプラグインやuseAsyncDataで置き換えます。

Nuxt.jsのnuxtServerInitアクションの使い方を紹介します。

Nuxt.jsのnuxtServerInitアクションの使い方

注意点としては、nuxtServerInitはstore/index.jsのactionsで使用します。
モジュール化している部分で使用してもうまく動作しません。
また、nuxtServerInitアクションはPromiseを返すかasync/awaitを使う必要があります。

    actions: {
      setPosts(vuexContext, posts) {
        vuexContext.commit('setPosts', posts)
      }
    },

元のstore/index.jsのactionsが上のような状態の場合、下のように追加します。

    actions: {
      nuxtServerInit(vuexContext, context) {

      },
      setPosts(vuexContext, posts) {
        vuexContext.commit("setPosts", posts);
      }
    },

nuxtServerInitの第一引数には、vuexContextをとります。
第二引数はNuxtのcontextです。context.req(リクエスト)、context.paramscontext.appcontext.$axiosのようにアクセスできるので、Cookieからログイン状態を復元したり、APIから初期データを取ったりするときに使います。

Promiseである必要があるので、returnでPromiseかasync/awaitを使います。

    actions: {
      nuxtServerInit(vuexContext, context) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            vuexContext.commit("setPosts", [
            {
              id: "1",
              title: "最初の投稿",
              previewText: "プレビューのテキスト",
              thumbnail:"サムネ"
            },
            {
              id: "2",
              title: "二回目の投稿",
              previewText: "二回目のプレビューテキスト",
              thumbnail:"サムネ"
            }
            ]);
            resolve();
          }, 1000);
        });
      },
      setPosts(vuexContext, posts) {
        vuexContext.commit("setPosts", posts);
      }
    },

async/awaitでAPIから取得する例

実務ではsetTimeoutではなくAPIを叩きます。async/awaitにすると次のように短く書けます。

    actions: {
      async nuxtServerInit({ commit }, { $axios, req }) {
        const posts = await $axios.$get("/api/posts");
        commit("setPosts", posts);

        // 例: Cookieに入っているトークンからログイン状態を復元する
        if (req && req.headers.cookie) {
          // cookieを解析してcommitする
        }
      },
    },

動かないときの確認

  • 呼ばれない: store/index.js以外(モジュール)に書いている、またはssr: false(SPAモード)で動かしている。nuxtServerInitはサーバー側でしか実行されません
  • データが反映される前に描画される: Promiseをreturnしていない、もしくはasyncを付け忘れてawaitが効いていない
  • ページ遷移のたびに走ってほしい: それはnuxtServerInitの役割ではなく、ページのasyncDatafetchで取得します
スポンサーリンク

Nuxt 3にはnuxtServerInitがない

Nuxt 3ではVuexが標準ではなくなり、nuxtServerInitも廃止されました。同じ「最初の一度だけサーバー側で初期データを用意する」は、次のどちらかで実現します。

  • Piniaなどのストアを、サーバー側で実行されるプラグインから初期化する
  • app.vueuseAsyncDataを使い、取得した値をuseStateやストアに入れる
// plugins/init.server.js  (末尾が.serverのファイルはサーバー側だけで実行される)
export default defineNuxtPlugin(async () => {
  const store = usePostsStore(); // Piniaのストア
  if (store.posts.length === 0) {
    store.posts = await $fetch("/api/posts");
  }
});

サーバーで入れたストアの状態はクライアントに引き継がれる(ハイドレーションされる)ので、Nuxt 2のnuxtServerInitと同じ体験になります。

レイアウト単位で共通の初期化をしたい場合はNuxt.jsで複数のlayoutsファイルを作成して適用する方法、取得した日付の表示整形はNuxt.jsで日付をフォーマットする方法を参考にしてください。

スポンサーリンク