nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

Nuxt.jsにおけるサーバーサイドレンダリングの挙動とライフサイクル

Nuxt.jsというかSSR関係を触る際にまず疑問になるのは「どこまでがサーバーサイドレンダリング (SSR) でどこからがクライアントサイドレンダリング (CSR) なの?」ということだと思います。

基本

簡単に言えば、ブラウザでURL直接指定で開けばSSR、それ以降はCSR、ということになります。

当たり前ですが、サーバーサイドレンダリングはサーバーでの実行なので、windowオブジェクトも存在しないし、そこから外部サーバー(APIサーバーなど)にアクセスする際はそこからのアクセスになります。そのままではブラウザのcookieも利用できません。

axios-moduleは何をしているのか

上記の通り、SSRになった場合とCSRになった場合は通信経路も変わるしcookieの取扱も変わります。

これらを真面目に対応すると結構大変なのですが、 axios-module を使うと大体のことを解決してくれます。

axios-moduleは大きく以下のようなことを行ってくれます。

  • SSRの場合とCSRの場合でエンドポイントを切り替える
  • クライアントサイドのHTTPヘッダ(cookieなど)をサーバーにそのまま透過的に送信する

他にもいろいろありますが、重要なのはこの2点で、これにより通信時のレンダリング場所がサーバーかクライアントかを意識せずにAPIアクセス処理を書くことができるようになります。Nuxtで開発する場合は必須と言っても過言ではないでしょう。

なお、複数のエンドポイントを指定したりはできないので、API通信先は1箇所に固定されることになります。複数のエンドポイントが発生しそうな場合はクライアント(Nuxt)だけで解決せず、BFF (Backends For Frontends) パターンを用いて処理するのが良いでしょう。

ライフサイクル

Nuxtを使うとコンポーネントにメソッドが増えたり、ミドルウェアプラグインのような仕組みが追加されます。これらがSSRの場合とCSRの場合でどのように呼ばれるかは使ってみないとわからないところです。

そこで、サンプルプロジェクト を用意し、調査してみました。

初回アクセス (SSR)

そこからの遷移 (CSR)

つまりどういうことか

Nuxtでアプリを作る際は、SSRでもCSRでも問題なく表示できるように作る必要があります。また、クローラーなどからのアクセスは常にSSRになるため、クローラーなどに拾ってほしい情報はSSRの段階で確定させる必要があります。

一方で、windowオブジェクトにアクセスするもの、例えばlocalStorageを使うようなものはクライアントサイドのみでしか実行することはできません。 mounted 以外で書く場合は process.server 変数などを使って分岐させる必要があります。

特に注意したいのが created で( beforeCreate も)、SSR時とCSR時の両方で呼ばれます。普通に書くと二重に処理してしまうことになるので、意識して書かないと意図しない挙動になる可能性があります。

まとめ

このあたりの挙動は分散して書かれてはいるものの、1ページにまとまっているものが見つからなかったため、今回用意してみました。

SSRは便利ではあるものの、どうしても層が1つ増えるために挙動が複雑になります。ちゃんと把握していれば怖くないので、どこで何が動いているのかを意識しながら書くようにしましょう。