はじめに
Next.jsに入門して1週間で学んだことを記事にまとめます。
なぜNext.jsなのか?
今年は自分でも何か作ってみたいと思っているのですが、エンジニアとしてのテーマとして、経験したことのないフレームワークやプログラミング言語を取り入れたいと考えています。
とはいえ、領域から大きく外れすぎるとなかなか完成しないと思ったので、開発するものはWebアプリケーションとして、その中で新しい学びのある技術を選定する中で、フロントエンドにNext.jsを使いたいと思いました。
Reactでも良い気はしましたが、開発するとしたら、to C向けで、SNSの要素のあるサービスを開発したいと考えていて、その場合はSEOに強かったり、外部でシェアされる際にOGPが効くのもNext.js強みだと思ったため、とりあえずNext.jsを学んでみることにしました。
Next.jsとは何か
Next.jsは、ReactベースのWebフレームワークです。Reactを単体利用する場合との最大の違いは、ページをコンポーネントをSSRできるかどうかという点になります。
例えば、create-react-appで生成されたアプリケーションをビルドすると、生成されるHTMLはindex.htmlのみで、実際に表示されるDOMはすべてJavaScriptによって描画されます(SPA – Single Page APplication)。
かつてはRailsで構築されたWebアプリケーションのように、テンプレートエンジンがHTMLを生成するタイプのMPA(Multiple Page Application)が全盛の時代もありましたが、画面遷移するごとにページそのものが遷移するため、もっさり感がありました。
そこにSPAが登場することで、いちいちページ遷移することの煩わしさが解消されるようになりましたが、SPAはJavaScriptによってページが描画されるため、HTMLを静的解析するタイプのクローラに情報を提供することが難しいという問題がありました。
GoogleはSPAのページも読み取ってくれるようですが、TwitterやFacebookなどでOGPを設定するのは難しいようで、パブリックアクセスが必要となるニュースサイトやECサイトなどでは、導入が難しかったのではないかと思います。
そこで、Next.jsの強みは、ページが読み込まれたタイミングで、初期レンダリングされたHTMLをブラウザ側に返すことができる点です。これについて、Next.jsではいくつかのアプローチがあり、その概要を説明します。
Static Site Generation (SSG)
Next.jsのページは、デフォルトでビルド時にPre-renderされ、静的なHTMLファイルとして出力されます。レンダリングされる内容は、各ページのReactコンポーネントの初回レンダリングの内容になります。そのため、stateによって変化する内容については、含まれません。
これが一番シンプルなSSGなのですが、実際のコンテンツはAPIで取得するケースがほとんどではないかと思います。その場合は、ビルド時にAPIリクエストし、その内容をページのPropsの初期値として設定することができる
getStaticProps
を使用することで、外部リソースを含めてPre-renderすることができます。
さらに、Vercelが提供している
useSWR
というhookを組み合わせて、アクセス時にクライアント側で最新データをフェッチすることで、ビルド後も常に最新の状態を表示することができます。
Incremental Static Regeneration (ISR)
SSGによってPre-renderされるのは、Componentに埋め込んだ内容か、ビルド時にフェッチしたデータです。ビルド時より後に追加されたデータはPre-renderには含まれないため、SEOやOGPで不利であることは、CSRとほとんど変わりません。
そこで、ブラウザによりページアクセスがあった際に一旦は古いHTMLを返しつつ、前回のHTMLの生成から一定時間が経過している場合は、最新のデータを取得しなおした上でHTMLファイルを再生成することができるのが、ISRです。
これにより、最新のデータを含めるために再ビルドし直すことなく、SSGによるパフォーマンスやSEO・OGPなどの恩恵を受けることができます。ただし、ISRを使用している場合は、トリガーとなったページへのレスポンスや、HTML生成中のアクセスでは古いHTMLがレスポンスされるため、タイミングによっては整合性が取れなくなります。
Server Side Rendering (SSR)
ISRではタイミングによっては整合性が取れない場合がありますが、リクエスト毎に最新データを取得しつつ、サーバサイドでレンダリングすることでSEO・OGPに対応するには、どのようにすれば良いでしょうか。
そのような場合は、
getServerSideProps
を使用します。この関数はページがリクエストされる毎にサーバサイドで実行され、この関数で返却されたPropsが渡された状態のコンポーネントがSSRされ、クライアントにレスポンスします。
あとは、SSGの時と同じように渡されたPropsをそのまま使ってコンポーネントを生成すれば、静的なHTMLとしてレスポンスされるため、SEO・OGPに対応することができます。
実際にNext.jsをセットアップ
Next.jsを特徴が分かってきたところで、実際にNext.jsを使ったアプリケーションを作ってみたいと思います。
create-next-app
create-next-app
を使用すると、次のように対話形式でセットアップすることができます。
1 2 3 4 5 6 | % yarn create next-app . ✔ Would you like to use TypeScript with this project? … Yes ✔ Would you like to use ESLint with this project? … Yes ✔ Would you like to use `src/` directory with this project? … No ✔ Would you like to use experimental `app/` directory with this project? … No |
後半2つの質問はNext.js特有のものになります。
Would you like to use
src/
directory with this project?
ここまで説明していなかったのですが、Next.jsではpagesディレクトリ配下の構成がそのままRoutingのパスとして解釈されます。デフォルトでは、pagesディレクトリはプロジェクトのルートに存在する必要があるのですが、これを
src/pages
に変更できるオプションです。
デフォルトではNoになっているため、私も今回はNoを選択しましたが、もしかしたらソースコードはsrcディレクトリの中にまとめてしまった方が、ルートがスッキリしてわかりやすくなるかもしれません。
Would you like to use experimental
app/
directory with this project?
Next.js 13で追加されたapp directoryを使用するかどうかです。現時点ではベータ版の機能です。app directoryを使用しない、従来のNext.jsでは、コンポーネントの初回レンダリングの結果をHTMLとしてレスポンスし、その後はクライアント側でCSRが行われるという流れが1つのコンポーネントで完結できていました。
app directoryを使用した場合は、コンポーネントをServer SideとClient Sideに明確に分けるようになります。デフォルトではServer Sideで、Client Sideにする場合はファイルの先頭に
use client
とする必要があります。
Server Sideコンポーネントの場合は、これまで通りサーバサイドでレンダリングされ、これまで通りデータフェッチは可能ですが、stateやイベントハンドリングといったインタラクティブなことはできません。そのような部分はClient Sideとして実装する必要があるのですが、Client Sideコンポーネントはサーバ側ではレンダリングできません。
このような明確な分け方をすることにより、SSRした時のパフォーマンスが向上するとのことですが、実装時にはServer SideとClient Sideを上手く使い分ける必要がありそうです。
Next.jsのドキュメントに使い分けの例があるので、こちらを参考にした方が良さそうです。
https://beta.nextjs.org/docs/rendering/server-and-client-components
今回は、app directoryを使用しないことにしました。その理由は、MUIが未対応であるからです。
https://github.com/mui/material-ui/issues/34896
MUIほどの人気パッケージでさえ未対応なので、app directoryは正式版になるまで待っても遅くはないかなと思い、今回は使用しないことにしました。
nextの起動
yarn dev
でnextアプリケーションを起動することができます。その他のコマンドは、以下がpacakge.jsonに定義されていました。
1 2 3 4 5 6 | "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" } |
ちなみに、
dev
で立ち上げた場合は、ページにアクセスされる毎に
getStaticProps
が呼び出されますが、
yarn build
でプロダクション用のビルドをすると、ビルド時に取得したHTMLが使用されるようになります。
さいごに
次回も引き続き学んだ内容を記事にしていきたいと思います!