こちらは Next.js Advent Calendar 2019 3日目の記事です。
ReactのSPAをNext.jsでSSRしてみて気づいたことをまとめてみました。
まだNext.jsやNuxt.jsでSSRやったことない人や、これからやろうか検討している人への情報共有的位置づけです。
目次
- SPAをSSRするとは?
- どのような構成になったか?
- どのような実装をおこなったか?
- SPAをSSRしてどうなったか?
- 気づいたこと
- まとめ
SPAをSSRするとは?
SPAの弱点である初回アクセス時の読み込み時間を短縮するために、サーバサイドでレンダリングを行いブラウザ上に素早く画面を表示させる手法。
初回表示後の画面遷移やアクションは通常のSPAと同じように機能する。
クライアントにおける初回表示のフローは、通常のSPAとSSRした場合とで以下のようになる。
通常のSPA
- サーバからバンドルされたJSファイルを取得
- Reactコンポーネントの初期化
- ローディング表示
- APIリクエスト&レスポンス
- 表示
SSRした場合
- サーバからデータが挿入されたDOMとバンドルされたJSファイルを取得
- 表示 (裏側で初期化がはしる)
通常のSPAではバンドルファイル取得後に順を追って初期化していくが、SSRすると取得したものをそのまま表示できるので、SPAなのにパッと画面が表示される。
どのような構成になったか?
通常のSPAではバンドルJSファイルをS3やFirebaseHostingなんかに置いてサーバレス構成にもできたが、SSRを行う場合はNode.jsのWebサーバをたてることになる。自分の場合は以下のような感じ。
- Next.jsで実装したアプリをExpressと組み合わせてWebサーバを構築
- GAEとかGKEのコンテナ上でサーバを立ち上げる
- サーバのルーティングもクライアントのルーティングもNext.jsが内部でやってくれる
どのような実装を行ったか?
基本的にNext.jsの作法に乗っかっていく感じ。公式リポジトリのexamplesがかなり充実しているので、主なケースは参考にしつつ実装できる。
ReactアプリをSSRする場合は具体的に以下のような改修を行っていく。
- componentDidMountやuseEffect(マウント時)で宣言していた処理をgetInitialPropsという関数で宣言する
- クライアントとサーバで違う処理を行う必要があるときはpropsのisServerを参照して処理を分ける
- Next.jsのルーティングシステムに合わせた構造にする(pagesディレクトリ配下にコンポーネント整備)
- サーバサイドからのAPIリクエストに使用する認証token(Cookie)をreqから取得する
- サーバにはwindowやdocumentがないので、ライブラリの初期化等で必要な場合は動的インポートを使用してSSRをエスケープする
- などなど……
SPAをSSRしてどうなったか?
初回表示がかなり速くなり、思わず「おおおぉ〜」と声がでた。
- ロード完了まで5秒ほどかかっていた画面が、1秒かからなくなった
- APIアクセスもサーバ同士なのでなんか速そう
- Next.jsのコード分割 & 動的インポートの機能をさらに活用していけばもっと速くなりそう
気づいたこと
通常のSPAでは(サーバレス構成にすれば特に)Webサーバについて気にする必要がなかったので、フロントエンドパワーがあればそれだけで完結する話が多かったように思う。
SSR構成にすればサーバ内の処理やリソースについても思いを巡らせる必要があるので、難易度も必要な工数も増すが、ある程度SPA開発に慣れていてサーバサイドに対しても明るいエンジニアであれば楽しんでいける領域だと思う。
Next.jsはSSRだけでなく静的ジェネレートやAMP、PWAについてもサポートしているので、Webアプリの理想郷を追求していける面白いフレームワークだと思う。
まとめ
- SSRはやっぱり速かった
- フレームワークの恩恵により、実装は意外と簡単だった
- サーバサイドをいかに攻略していけるかがポイント
- 技術力さえあれば最高のWebアプリケーションが作れると思う!
以上です。ここまで読んでいただき、ありがとうございました。
2019年のアドベントカレンダーは3つ参加してます。よかったら他の記事も見てください。
- Next.js Advent Calendar 2019 3日目 …… Next.jsをつかってSPAをSSRしてみた感想(この記事)
- React Advent Calendar 2019 7日目 …… Reactの歴史
- Go4 Advent Calendar 2019 12日目 …… Goでライフゲームを実装してみた
P.S. @tkdnさん: カレンダーへご招待いただきありがとうございました。