Nuxt.jsでの完全静的生成(Full Static Generation)がうまく機能していないケースがあった。
状況と原因を理解するのに丸一日かかってしまった。検索してもそれに該当する情報に行き当たらなかったので、やり場のない怒りを供養するために、ここに放流する。
環境
Nuxt.js 2.15.8
Full Static Generationとは何か
デプロイ時に全てのデータをソースファイルに組み込んでサイト生成をすることである。
それの何が良いのか。
CMSなどのウェブAPIからサイト掲載データを取得する場合、
ページロード時にクライアント(ユーザーPC)からAPIにアクセスすると、ブラウザの開発者ツールを使えばAPIキーが見えてしまう。
完全静的生成ならば、あらかじめAPIから取得したデータを含めてビルドするので、クライアントからAPIアクセスする必要がなくなり、APIキーを隠蔽できるわけだ。
詳細は公式。
https://nuxt.com/blog/going-full-static
どうやって実現するか
- nuxt.config.jsに
target: "static"
を追加 - nuxt.config.jsの
privateRuntimeConfig
にAPIキーなど、隠蔽したい情報を指定 - ページコンポーネントで、
asyncData
でAPIアクセスしてデータを取得 -
nuxt generate
でビルド実行
// nuxt.config.js
modules.exports = {
mode: "universal",
target: "static",
// ...
privateRuntimeConfig: {
apiKey: 'xxxxxxxx',
},
}
// pages/index.js
exports default {
async asyncData({ $config }) {
// $config に privateRuntimeConfigで指定したプロパティが入っている
console.log('fire asyncData');
const data = await axios.get(
'https://example.com/',
{ apiKey: $config.apiKey }
);
return {
data,
};
},
}
asyncData()
は、コンポーネントがcreateされる前にサーバサイドでのみ実行される。
そのためSSGの場合には、ビルド時のみ実行され返却値が埋め込まれることになる。
私の環境ではどうなったか
ビルド後のサイト(/dist)にアクセスしてみたところ、
apiKeyがないというエラーが表示されると同時に、なんと asyncData がブラウザ上で実行されている(consoleにlogが出ている)のだ。
「SSGでasyncDataが実行される(asyncData of Nuxt.js is called on Static genarated page)」
とかで検索しても全然出てこない。
どこの記事でも、SSGの場合には、asyncDataはビルド時にのみ実行されて、ページ表示時には実行されないと書いてある。
いやいやいや、実行されているんですけれども。
ページ表示時にasyncDataが実行されるということは、つまりビルド時には実行されていない
すなわち、APIからデータを取得できていないということだ。
そして、ブラウザで実行されているので、privateRuntimeConfigの値もundefined
だ。
公式ドキュメントでは、privateRuntimeConfig
で指定した値は、asyncData
の中で$config
で取れるよ、と素朴に書いてある。できねーじゃんよ。
なぜSSGなのに、asyncDataが実行されるか。
私の環境における原因は、@nuxtjs/auth-nextを使った認証機能を組み込んでいるからであった。
認証機能に限らないのだろうが、おそらくmiddlewareを組み込んでいるとfull static generationされないのだと思われる。
middlewareは、ページを表示する際に都度実行されなければならない。
認証機能を考えればわかるが、「あらかじめ処理しておく」ということはできない。
middlewareの処理は、asyncDataより先なので、asyncDataもビルド時あらかじめ実行しておいて返り値を埋め込んでおく、ということができない。ゆえに、Full Staticな生成ができなくなる、ということなのであろう。
(状況から考えた雑な理解なので、間違っていたら指摘してもらえると助かります)
どうするか
私の場合、当初の目的はAPIキーの隠蔽であった。
それは、Nuxt.jsの機能として実装することは諦め、別途自前で処理を書くことにする。
APIにアクセスして取得したデータをJSONファイルとして保存するコードを書く、自前で。
そして、ビルド時にnuxt generate
コマンドを実行する前に、その処理を走らせる。
Nuxt.jsのページでは、保存したそのJSONファイルからデータを読み込むこととする。
フレームワークに疲弊させられるというのは気分が良くない。やれやれ。