【Nuxt】SSGとSPAの『nuxt generate』やアプリケーションの挙動の違いについて比較してみる

JavaScript

こんにちは。Enjoy IT Life管理人の仁科(@nishina555)です。

前回、『【Nuxt】SSR・SSG・SPAにおける『nuxt build』と『nuxt generate』の実行結果の違いまとめ』で、Nuxtで用意されているSSR(Server Side Rendering)、SSG(Static Site Generator)、SPA(Single Page Application)の計3つのモードについて紹介しました。

【Nuxt】SSR・SSG・SPAにおける『nuxt build』と『nuxt generate』の実行結果の違いまとめ

静的ウェブサイトで運用する場合はSSGかSPAを選択できます。

SSGとは事前に生成した静的ファイルを利用して画面を表示するモードです。

SSGは事前に各画面の静的ファイルを生成するため画面表示が早い反面、コンテンツを更新するたびにビルドが必要という欠点があります。

ランディングページをはじめとした更新頻度の少ないページとSSGは相性がよいです。
一方、同じURLでもユーザーによって画面の見え方の異なるページや、更新頻度の多いページとの相性はあまりよくありません。

SPAとはサーバーサイドでレンダリングを行わず、フロントエンドで画面を組み立てるモードです。

SPAは差分更新のみで画面を変更するため画面遷移が高速である反面、JavaScriptを読み込むため初回のページロードに時間がかかるという欠点があります。

SSGとSPAの実装は異なりますが、『nuxt generateで生成された成果物を静的ウェブサイトへデプロイする』というプロセスは一緒です。
SSGとSPAの定義がわかっていてもプロセスが同じであるため、言葉だけだと違いがイマイチわかりにくいです。

そこで今回はnuxt generateによる成果物やNuxtアプリケーションの挙動がどのように異なるのか具体例をもとにSSGとSPAの違いについて紹介します。

なお、Nuxtは2.14.1を利用します。

検証に利用するサンプルアプリケーションについて

SSG・SPAにおけるnuxt generateおよびNuxtアプリケーションの挙動を確認するにあたり、今回はNuxt公式ドキュメントで紹介されているCustom Routesを利用します。

Custom Routesはユーザー一覧画面と各ユーザーの詳細画面が用意されているシンプルなアプリケーションです。

『nuxt generate』による成果物の違いについて

nuxt generateの実行プロセスや成果物の違いについて紹介します。

SSGの場合

nuxt generateの実行ログは以下の通りです。
Generated route "/users/xx"というログからわかるように、各ページの静的ファイルが生成されています。

ℹ Doing webpack rebuild because nuxt.config.js modified
ℹ Production build
ℹ Bundling for server and client side
ℹ Target: full static
✔ Builder initialized
✔ Nuxt files generated

✔ Client
  Compiled successfully in 6.12s

✔ Server
  Compiled successfully in 624.21ms


Hash: 05e1720be351433f6124
Version: webpack 4.44.1
Time: 6121ms
Built at: 2020/08/10 14:20:38
                          Asset       Size  Chunks                         Chunk Names
 ../server/client.manifest.json   7.86 KiB          [emitted]
                       LICENSES  389 bytes          [emitted]
                 app.b67a212.js   58.5 KiB       0  [emitted] [immutable]  app
node_modules/commons.501805f.js    168 KiB       1  [emitted] [immutable]  node_modules/commons
         pages/index.fe29326.js   1.51 KiB       2  [emitted] [immutable]  pages/index
     pages/users/_id.7d0c948.js   1.45 KiB       3  [emitted] [immutable]  pages/users/_id
             runtime.715f042.js   2.35 KiB       4  [emitted] [immutable]  runtime
 + 2 hidden assets
Entrypoint app = runtime.715f042.js node_modules/commons.501805f.js app.b67a212.js

Hash: 93b14cb09a45b6c86e9a
Version: webpack 4.44.1
Time: 625ms
Built at: 2020/08/10 14:20:39
               Asset       Size  Chunks             Chunk Names
      pages/index.js   6.63 KiB       1  [emitted]  pages/index
  pages/users/_id.js   6.59 KiB       2  [emitted]  pages/users/_id
           server.js   87.2 KiB       0  [emitted]  app
server.manifest.json  307 bytes          [emitted]
 + 3 hidden assets
Entrypoint app = server.js server.js.map
ℹ Generating output directory: dist/
ℹ Generating pages with full static mode
✔ Generated route "/"
✔ Generated route "/users/1"
✔ Generated route "/users/2"
✔ Generated route "/users/10"
✔ Generated route "/users/4"
✔ Generated route "/users/6"
✔ Generated route "/users/5"
✔ Generated route "/users/7"
✔ Generated route "/users/9"
✔ Generated route "/users/8"
✔ Generated route "/users/3"
✔ Client-side fallback created: 200.html
✨  Done in 10.77s.

静的ファイルの出力先であるdist配下は以下のようになっています。
ルートのindex.htmlだけでなく、ユーザー詳細画面(users/:id/index.html)の静的ファイルも存在しています。

$ cd dist
$ tree -L 6

.
├── 200.html
├── README.md
├── _nuxt
│   ├── LICENSES
│   ├── app.b67a212.js
│   ├── node_modules
│   │   └── commons.501805f.js
│   ├── pages
│   │   ├── index.fe29326.js
│   │   └── users
│   │       └── _id.7d0c948.js
│   ├── runtime.715f042.js
│   └── static
│       └── 1597036839
│           ├── payload.js
│           └── users
│               ├── 1
│               │   └── payload.js
│               ├── 10
│               │   └── payload.js
│               ├── 2
│               │   └── payload.js
│               ├── 3
│               │   └── payload.js
│               ├── 4
│               │   └── payload.js
│               ├── 5
│               │   └── payload.js
│               ├── 6
│               │   └── payload.js
│               ├── 7
│               │   └── payload.js
│               ├── 8
│               │   └── payload.js
│               └── 9
│                   └── payload.js
├── favicon.ico
├── index.html
└── users
    ├── 1
    │   └── index.html
    ├── 10
    │   └── index.html
    ├── 2
    │   └── index.html
    ├── 3
    │   └── index.html
    ├── 4
    │   └── index.html
    ├── 5
    │   └── index.html
    ├── 6
    │   └── index.html
    ├── 7
    │   └── index.html
    ├── 8
    │   └── index.html
    └── 9
        └── index.html

SPAの場合

nuxt generateの実行ログは以下の通りです。
SPAの場合はSSGと違い、静的ファイルはルート(/)のみ作成されています。

ℹ Doing webpack rebuild because nuxt.config.js modified
ℹ Production build
ℹ Bundling only for client side
ℹ Target: static
✔ Builder initialized
✔ Nuxt files generated

✔ Client
  Compiled successfully in 6.96s


Hash: 47d36e3c6f1bf3363c94
Version: webpack 4.44.1
Time: 6961ms
Built at: 2020/08/10 14:18:13
                          Asset       Size  Chunks                         Chunk Names
 ../server/client.manifest.json   7.79 KiB          [emitted]
                       LICENSES  389 bytes          [emitted]
                 app.ef33196.js   55.4 KiB       0  [emitted] [immutable]  app
node_modules/commons.48315ec.js    168 KiB       1  [emitted] [immutable]  node_modules/commons
         pages/index.9ce75e2.js   1.51 KiB       2  [emitted] [immutable]  pages/index
     pages/users/_id.5722ee2.js   1.45 KiB       3  [emitted] [immutable]  pages/users/_id
             runtime.84feac3.js   2.35 KiB       4  [emitted] [immutable]  runtime
 + 1 hidden asset
Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.ef33196.js
ℹ Generating output directory: dist/
ℹ Generating pages
✔ Generated route "/"
✔ Client-side fallback created: 200.html
✨  Done in 11.78s.

静的ファイルの出力先であるdist配下は以下のようになっています。
SPAではユーザー詳細画面(users/:id/index.html)の静的ファイルは存在していません。

$ cd dist
$ tree -L 4

.
├── 200.html
├── README.md
├── _nuxt
│   ├── LICENSES
│   ├── app.ef33196.js
│   ├── node_modules
│   │   └── commons.48315ec.js
│   ├── pages
│   │   ├── index.9ce75e2.js
│   │   └── users
│   │       └── _id.5722ee2.js
│   └── runtime.84feac3.js
├── favicon.ico
└── index.html

Nuxtアプリケーションの挙動の違いについて

実際に起動させたアプリケーションを例に、挙動の違いについて紹介します。

SSGの場合

実際の画面がプレビューで表示されていることから分かるとおり、SSGではレンダリングされた画面がレスポンスとして返ってきます。

レスポンスデータにも具体的な値がすでに書き込まれています。

ユーザー詳細画面も同様です。

このように、SSGでは各画面の静的ファイルを事前に生成しておきレスポンスとして返します。
ユーザー情報なども静的な情報として書き込まれているため、SSGではデータの更新がある度に静的ファイルを再生成する必要があります。

SPAの場合

実際の画面がプレビューに表示されていないことから分かるとおり、SPAでは画面の作成はフロントエンド(ブラウザ)で行っています。

SSGではレスポンスに具体的な値が書き込まれていましたが、SPAの場合はJavaScriptが組み込まれています。

ユーザー詳細画面も同様です。

このように、SPAではJavaScriptの埋め込まれたindex.htmlをレスポンスとして返します。
フロントエンドでJavaScriptを読み込むことで画面を表示したり画面遷移を実現したりしています。

まとめ

以上で具体例を用いたSSG・SPAの違いの紹介を終わります。

今回のまとめ
  • SSGでは『nuxt generate』で各画面の静的ファイルが生成される
  • SAPでは『nuxt generate』で生成される静的ファイルはルートのみ
  • SSGでは静的ウェブサイトからのレスポンス時に画面が作成されている
  • SPAでは画面の作成はブラウザ上で行われる

この記事がいいなと思いましたらTwitter(@nishina555)のフォローもよろしくお願いします!