Nuxt.jsでVuexを利用しているとき、fetch()によってコンポーネントがロードされるたびにサーバーサイドレンダリングでデータを取得したくない場合があります。
最初の一度だけサーバーサイドでレンダリングしたいときに、Nuxt.jsの特別なアクションであるnuxtServerInitを使います。
結論から言うと、nuxtServerInitはNuxt 2 + Vuexで使える特別なアクションで、store/index.jsのactionsに書くと、サーバー側でページを描画する前に一度だけ自動で呼ばれます。第一引数はVuexのcontext(commitなどを持つ)、第二引数はNuxtのcontext(req、params、appなど)です。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.params、context.app、context.$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の役割ではなく、ページの
asyncDataやfetchで取得します
Nuxt 3にはnuxtServerInitがない
Nuxt 3ではVuexが標準ではなくなり、nuxtServerInitも廃止されました。同じ「最初の一度だけサーバー側で初期データを用意する」は、次のどちらかで実現します。
- Piniaなどのストアを、サーバー側で実行されるプラグインから初期化する
app.vueでuseAsyncDataを使い、取得した値を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で日付をフォーマットする方法を参考にしてください。