はじめに
Next.js に入門するべく 公式ドキュメントの How Next.js Works を読んだので、冷めないうちにアウトプットを残します。
ドキュメントの本文は英語で書かれており、DeepL 翻訳をかけても多少読みづらい文体です。
そのため、筆者の理解に合わせて読みやすい文章に校正しています。
上記のドキュメントでは Next.js に限定せず、フロントエンドで使用されている技術として説明されています。
各概念の説明が「一般論」+「Next.js の場合」の2部構成になっているため、広い意味での概念を抑えることができるとともに Next.js での利用イメージも掴むことができます。
かなりの良書です。
とりあえず Next.js で Hello World を体感してみたい方は From React to Next.js がおすすめです。
From React to Next.js の学習時のコードは GitHub のリポジトリ にアップロードしています。
Next.js
Next.js の特徴には以下の3点がある。
- 環境
- 開発環境と本番環境
- コード実行
- ビルドタイムとランタイム
- レンダリング箇所
- クライアントとサーバー
1つ1つ見ていきましょう🙌
環境
環境とは、コードが実行されているコンテキストのことである。
Next.js では、2つの環境に対応する。
- development
- ローカルマシンでのアプリケーション構築、実行
- production
- デプロイし、ユーザーが使用する環境
それぞれの環境では目的が異なるため、開発→本番への環境の移行には多くの作業が必要となる。
具体的には、コンバイル、バンドル、最小化、コード分割などを行う必要がある
しかし、これらの作業は Next.js のコンパイラが担当することにより、開発者はよりプロダクトの開発に集中できるようになっている。
Development
主に開発者とアプリケーション構築、開発者体験に対して最適化される環境である。
例えば、以下のことが行われる。
- TypeScript と ESLint の統合
- Fast Refresh
- ソースコードが変更されると、即座にブラウザに結果を反映する
Production
主にエンドユーザー、ユーザー体験に対して最適化される環境である。
例えば、以下のことが行われる。
- コードの変換
- パフォーマンスとアクセスの向上
コンパイラ
Next.js は、コードの変換や基礎的なインフラの多くの処理を担当する。
Next.js は以下の2つを備えている。
- コンパイラ
- Rust 製で高速に動作する
- SWC
- コンパイル
- ミニフィケーション
- バンドル
コンパイル
コンパイルとは、ある言語で書かれたコードを、別の言語または別のバージョンで出力するプロセスを指す。
開発者は JSX や TypeScript などの開発者に優しい言語でコードを書くが、ブラウザは JavaScript のみを理解する。
そのため、開発者が書いたコードを JavaScript に変換する処理が必要である。
コンパイルは以下のタイミングで行われる。
- 開発段階でのコードの編集
- 本番環境前のビルドステップ
ミニフィケーション
ミニフィケーション(最小化)とは、コードの機能を変えずに不要なコードを削除するプロセスを指す。
開発者は人間が読みやすいコードを書くが、コンピューターにとってコメントやスペース、インデントは不要な記述である。
そのため、アプリケーションの実行時には予め不要な記述を取り除くことにより、ファイルサイズを小さくしてパフォーマンスを向上させる。
バンドル
バンドルとは、ファイルの依存関係を解決し、ブラウザに最適化された1ファイルに結合するプロセスを指す。
バンドルの目的は、ユーザーがページへ訪れたときにリクエストするファイル数を減らし、リクエストをより高速にすることである。
開発者は、アプリケーションをモジュール、コンポーネント、関数に分割することにより、開発生産性を向上させようとする。
その結果、ファイルの数が増え、複雑なファイルの依存関係が構築される。
ファイルの数が増えると、ユーザーがページへ訪れた際にリクエストするファイル数も増えることになる。
コード分割
コード分割は、アプリケーションのバンドルを、各エントリーポイントに必要なチャンクに分割するプロセスを指す。
エントリーポイントとは、各ページにアクセスするための位置である。
開発者は、アプリケーションを複数のページに分割し、異なるURLからアクセスできるように、一意なエントリーポイントを設定する。
コード分割の目的は、ページに必要なコードのみを読み込み、アプリケーションの初期ロード時間を改善することである。
ページ間で共有されるコードも、ページごとに別のチャンクに分割される。
全体ではコードが重複してしまうが、ページを移動する際に既に手元に同じコードがあればそのコードを使い回すことができるため、より高速なレスポンスを返すことができる。
ビルドタイムとランタイム
ビルドタイム
ビルドタイムとは、アプリケーションコードを本番環境で動作されるために準備する一連のステップのことを指す。
アプリケーションをビルドすると、運用に最適化された3種類のファイルに変換される。
- HTML ファイル
- 静的に生成されたページのためのファイル
- JavaScript コード
- サーバーでページをレンダリングするためのコード
- クライアントでページをインタラクティにするためのコード
- CSS ファイル
ランタイム
ビルド、デプロイを行った後の、アプリケーションが実行される期間を指す。
クライアントとサーバー
クライアント
ユーザーのデバイス上のブラウザのことを指す。
クライアントは、アプリケーションコードのリクエストをサーバーに送信する。
また、サーバーからのレスポンスをユーザーが操作できるインターフェースに変換する。
サーバー
アプリケーションコードを格納し、レスポンスを返すコンピュータのことを指す。
クライアントからリクエストを受け取り、レスポンスを返す。
レンダリング
レンダリングとは、React などで記述したコードを HTML に変換する処理のことを指す。
このレンダリングはサーバー上でもクライント上でも行われる。
さらにビルド時に前もって行われることもあれば、ランタイム時にリクエストごとに行われることもある。
Next.js では、3種類のレンダリング方式を選択できる。
- SSR (Server-Side Rendering: サーバーサイドレンダリング)
- SSG (Static Site Generation: 静的サイト生成)
- CSR (Client-Side Rendering: クライアントサイドレンダリング)
Next.js の魅力は、ユースケースに応じて最適なレンダリング方法を選択できることである。
ユースケースに応じたレンダリング方法の選択に関しては data fetching docs を参照。
プリレンダリング
SSR(サーバーサイドレンダリング) と SSG(静的サイト生成) は、プリレンダリングとも呼ばれる。
プリレンダリングとは、レンダリングをサーバー上で行うプロセスのことであり、すなわち、予めサーバー上で HTML を生成しておく方式のことである。
Next.js では、デフォルトですべてのページがプリレンダリングされる。
SSR(サーバーサイドレンダリング)
SSR(サーバーサイドレンダリング) では、リクエストごとにサーバー上でページのHTMLが生成される。
SSR(サーバーサイドレンダリング)では、以下の 3つ がクライアントに送られる。
- 生成されたHTML
- JSONデータ
- ページをインタラクティブに動かすためのJavaScript
インタラクティブでないページは生成された HTML をそのまま表示すれば良いため、クライアント側ではより高速に表示できる。
Next.js では、getSErverSideProps を使用することで、SSR(サーバーサイドレンダリング) を行うページを選ぶことができる。
ハイドレーション
ハイドレーションとは、コンポーネントをインタラクティブにする処理のことである。
例えば、ボタンにイベントハンドラを付ける処理などが該当する。
これにより、ボタンにホバーしたとき、ボタンを押したときなど、ユーザーの操作に対してリアルタイムにフィードバックを送ることが可能となる。
React では、JSON データと JavaScript の命令を利用してハイドレーションを行う。
SSG(静的サイト生成)
SSG(静的サイト生成) では、HTML はビルド時に生成される。
その後、CDN(コンテンツデリバリーネットワーク) に保存され、リクエストごとに再利用される。
つまり、アプリケーションの実行時にサーバー上には存在しない。
Next.js では、getStaticProps を利用して SSG されるページを選択することができる。
リアルタイムレンダリング
CSR(クライアントサイドレンダリング)
CSR(クライアントサイドレンダリング) とは、最初のレンダリング処理がユーザーのデバイスで行われる処理のことである。
標準的な React アプリケーションでは、ブラウザはサーバーからは空の HTML とUIを構築するための JavaScript を受け取る。
その後、レンダリング処理はクライアント上で行う。
Next.js では、以下のようなフックを利用することより CSR(クライアントサイドレンダリング) を使用することができる。
- useEffect()
- useSWR
ネットワーク
ネットワークとは、リソースを共有できるようにリンクされたコンピューターだと考えることができる。
Next.js の場合、アプリケーションコードはオリジンサーバー、CDN、エッジに分散させることができる。
オリジンサーバー
サーバーとは、アプリケーションコードのオリジナルバージョンを保存し、実行するメインコンピュータのことを指す。
このサーバーを、他のアプリケーションコードを配布できるサーバー(CDNサーバーやエッジサーバー)と区別するために、オリジンサーバーと呼ぶことがある。
オリジンサーバーは、リクエストを受信すると、何らかの計算を行い、レスポンスを返す。
この計算結果は、CDN(コンテンツデリバリーネットワーク) に転送することができる。
CDN(コンテンツデリバリーネットワーク)
CDN(コンテンツデリバリーネットワーク) とは、静的コンテンツ(HTML や画像ファイル)を高速に配信するためのサーバーである。
静的コンテンツは世界中の複数の場所に保存され、クライアントとオリジンサーバーの間に置かれる。
新しいリクエストが来ると、ユーザーから最も近い CDN(コンテンツデリバリーネットワーク) の拠点が、キャッシュされた結果をレスポンスすることができる。
リクエストごとに計算を行う必要がなくなるため、オリジンサーバーの負荷が軽減される。
ユーザーは、地理的に近い場所からレスポンスを受け取るので、より早くページを表示できる。
エッジ
エッジとは、ユーザーに最も近いネットワークの端を意味する一般的な概念である。
CDN(コンテンツデリバリーネットワーク) は、ネットワークの端に静的コンテンツを保存するため、エッジの一部とみなすことができる。
エッジサーバーも、CDN と同様に世界中の複数の場所に分散して配置されている。
しかし、CDN とは異なり、エッジサーバーの中にはコードを実行できるものもある。
エッジサーバーでは、キャッシュとコード実行の両方を、ユーザーにより近い場所で行うことができる。
エッジサーバーの利用により、従来のクライアントサイドやサーバーサイドの作業の一部をエッジに移すことができ、クライントに送信されるコード量が減る。
Next.js では、Middleware によってEdgeでコードを実行することができ、さらに React Server Components を使うこともできる。
Next.js でのエッジの利用例は Edge Functions にある。
終わりに
いかがでしょうか。
原典から多少の加筆修正は加えましたが、それでも辞書チックで少し読みづらくなってしまったなと感じています。
フロントエンドの技術では略語が多く(SSR や CDN など)、筆者も初めて学習したときには苦労しました。
そのため、本文ではなるべく括弧の中に元の名称を記述しています。
Next.js の学習のお役に立てましたら幸いです。