これから始める、Next.js 第1回 Next.jsとは
Next.jsの特徴を簡単なデモを使って確認してみます。Reactをベースとし、用途に合わせSSRやSSGを組み合わせた柔軟なサイト書き出し機能を試してみましょう。
- カテゴリー
- UIフレームワーク >
- React/Preact
発行
はじめに
Next.jsは、Reactをベースとしたフレームワークです。アメリカに籍を置くVercel社によって管理されているOSSプロジェクトでもあります。
この記事の執筆時点では10.0.5
が最新バージョンとなっています。
Vercel社は、Webサイトのデプロイ・ホスティングサービスとしての「Vercel」も会社と同じ名前で運営しており、Next.jsとVercelを合わせて利用することで簡単にハイパフォーマンスなWebサイトが作れるということを掲げています。
Vercel社は2016年にZEITという名前でスタートして、ホスティングサービスのnowを運営していました。同年にNext.jsの発表もしています。そして2020年の4月に社名とサービスをVercelという名前に統一しました。その後、10月には初めての世界規模カンファレンスであるNext.js Conf 2020をオンラインで開催、12月にはVercel社が40億円の資金調達を行うなど、大きな勢いを持った製品となっています。
参考リンク
Next.jsのWebサイト( https://nextjs.org/ )。
Reactに限らずSPA(Single Page Application)では、Webサイトへ訪問した瞬間の時点では画面すべての情報は表示されません。少しローディングが走って、表示可能になったコンポーネントから順に表示されるという見え方が多いと思います。これはブラウザ上で発生するイベントや、ユーザーの操作に応じてサーバーから取得した情報を、ブラウザ側で実行されるJavaScriptの処理によって随時HTMLを書き換えながら画面が更新されていくためでした。
そうしたSPAに対して、最初に表示される情報が適用されたHTMLを、ブラウザからのリクエストに応じてサーバー側で生成して返却するSSR(Server-side Rendering)という手法を簡単に導入できるものとしてNext.jsは登場しました。
こうしてSSRフレームワークとして名前が知られる存在でしたが、現在では静的なHTMLを書き出すSSG(Static Site Generation)や、静的なHTML生成をブラウザからのリクエストがあった段階で行うISR(Incremental Static Regeneration)といった、Webサイトのさまざまな構築方法をサポートするようになっています。
SPA・SSR・SSG
SPA
SPA(Single Page Application)は、単一のWebページから構成されるWebアプリケーションです。サーバーは最初にアプリケーションの動作に必要なHTML・CSS・JSなどのアセットをブラウザへ返却します。ブラウザはその上で起こるイベントやユーザーの操作に応じて必要なデータをサーバーにリクエストしながら、画面遷移を行わずにUIを更新していきます。前節で触れたSSRと対比して、CSR(Client-side Rendering)と呼ばれることもあります。
SSR
SSR(Server-side Rendering)では、SPAにおける初期状態が初めから適用されたHTMLをサーバー側で構築して返却します。ブラウザはそのHTMLを表示しつつも、それらをサーバーから同時に返されている初期状態をセットしたReactコンポーネントに置換することで、以降はSPAとして動作します。
このときのSSRという言葉にはSPAを経た文脈を持ち合わせています。従来の、サーバーサイドのフレームワークでHTMLテンプレートにデータを適用してHTMLを返すような方法とは少し異なる意味合いとなります。
SSG
SSG(Static Site Generation)は、用意されたテンプレートにデータを当てはめて事前にHTMLを書き出します。これ自体は新しいものではありませんし、静的サイトジェネレータと呼ばれるカテゴリのツールも以前から多くあります。しかしNext.jsなどのフレームワークによるサポート、CI/CD環境の充実、ヘッドレスCMSの発達などを追い風としながら、セキュリティやパフォーマンス上のメリットも見直されるなどして再び注目されています。
なぜNext.jsなのか
Next.jsはもともとSSR用フレームワークなので、ReactでSSRしたいからNext.jsを使うという動機が素直なものです。なぜSSRをしたいのかといえば、SPAの初期表示速度の改善や、検索エンジンから読めるHTMLを返せることによる検索エンジンの最適化、OGP対応のためというケースはよく聞かれます。
現在では、SSRで表示する内容がビルド時に確定できるなら、その時点で静的なHTMLを書き出せばよいというトレンドが強まりつつあります。Next.jsもSSGの機能によって、そうした需要への対応ができています。
Next.jsによるSSGは、ReactコンポーネントがHTML書き出しのためのテンプレートとして、またブラウザ上では従来のSPAのようにユーザーの操作と相互に作用します。ただHTMLのみを書き出すツールの場合、JSを使った処理についてライブラリを使おうか、使うならどれにしようかといった考え事が生まれますが、Next.jsの場合にはそれを気にする必要はありません。
SSRかSSGか? という話題があまり関係ないケースにおいても、開発効率やパフォーマンスに優れたReactプロジェクトを始めるためにNext.jsを採用する選択肢は考えられます。ルーティング機能を内包していることや、webpackの設定を自分で用意せずとも、さまざまなパフォーマンス最適化が組み込まれたReactプロジェクトを簡単に始められます。
Next.jsを始めてみる
今回の記事では、とりあえずNext.jsを動かしてみるところから紹介していきたいと思います。
Next.jsは簡単に始めるためのセットアップ用パッケージである「create-next-app」もnpmに公開されています。便利ですが、これを利用した場合には初めから多くのファイルが自動的に作られます。この記事ではもう少し順を追って説明したいため、手動による最小構成なセットアップを紹介します。
まずは作業用のディレクトリを作成して、package.json
のある状態にしておきます。ここではnpm init
を実行してpackage.json
を作成しました。
ディレクトリの作成とpackage.jsonの作成
$ mkdir nextjs-example
$ cd nextjs-example
$ npm init -y
npmからnext
とreact
とreact-dom
の3つのパッケージをインストールします。
必要なnpmパッケージのインストール
$ npm install next react react-dom
執筆時点でインストールされるパッケージのバージョンは、それぞれ次のとおりです。
next:10.0.5
react:17.0.1
react-dom:17.0.1
インストールができたら、よく使うコマンド*をpackage.json
のscripts
に設定しておきます。
package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next export"
}
}
注:Next.js CLI
Next.jsは本番用ビルドや開発モードとしての起動などを行うコマンドも提供しています。
Next.jsに特有の、Git管理から除外しておくディレクトリも.gitignore
に列挙しておきます。これらは、この記事で後に紹介するnext build
やnext export
を実行した際に書き出されたファイルが置かれるディレクトリです。
.gitignore
.next/
out/
次に最初のページとして、pages/index.js
を作成します。Next.jsはファイルシステムベースなルーティングの仕組みを持っており、pages
ディレクトリ配下に配置したファイル・ディレクトリの構造を基にWebサイトのルートを構築します。
pages/index.js
からエクスポートしたReactコンポーネントはトップページとして働きます。
$ mkdir pages
$ touch pages/index.js
index.js
からはデフォルトエクスポートでReactコンポーネントを公開します。
pages/index.js
const RootPage = () => {
return <h1>It works!</h1>;
};
export default RootPage;
この状態で開発モードのローカル起動をしてみるため、npm run dev
を呼び出してみましょう。先ほどpackage.json
のscripts
を設定してあるので、これによってnext dev
コマンドが実行されます。
$ npm run dev
npm-scriptsの使い方
package.json
のscripts
プロパティには、好きなコマンドの呼び出しを設定しておくことができます。
Next.jsのnpmパッケージであるnext
をインストールすると、next
コマンドも使えるようになります。その本体は./node_modules/.bin/next
にあります。すると、これを呼び出すには次のようなコマンドになるわけですが、これは少し長いです。
$ ./node_modules/.bin/next dev
そこで、次のようにpackage.json
のscripts
にnext dev
の呼び出しを設定をしておけば、npm run dev
を呼び出すだけで済むようになります。
{
"scripts": {
"dev": "next dev"
}
}
npm run <command>
の実行時には自動的に./node_modules/.bin
へのパスが通った状態になるため、package.json
の中ではそのことは気にせずに、ただnext dev
を呼び出すこと、という紐付けだけを行えば十分です。
次のログが表示されたら、http://localhost:3000
にアクセスすることで、現在のローカル環境で動いているWebサイトが見られます。
ready - started server on http://localhost:3000
event - compiled successfully
ローカル環境で動作しているNext.js。
Next.jsの開発モード
いまpackage.json
のnpm scripts経由で実行したnext dev
コマンドは、ローカル環境での開発をサポートするためのサーバーの起動と、変更されたコードの差分更新機能や、エラーのわかりやすい表示などの機能を持っています。
このコマンドによって動いているWebサイトをブラウザで開いている間は、手元のファイルを編集するだけで、ブラウザ側の操作をしなくてもNext.jsがその変更を検知してWebサイトに最新の状態を反映します。
また、ページのレンダリング中や通常の動作中に発生したエラーもWebサイトの画面に割り込む形で通知されます。
ページのレンダリング中に発生したエラーの通知。
ポートはデフォルトで3000番が利用されますが、-p
オプションによる指定が可能です。
ポート指定を行ったnext devの実行
$ next dev -p 3001
Next.jsの本番モード
本番環境でNext.jsを動かす場合には本番モードで起動させます。それにはnext start
を呼び出すことになりますが、その前にnext build
コマンドでビルドを行っておく必要があります。
先ほどpackage.json
に、npm run build
によってnext build
が呼び出されるように設定してあるので、次のように実行してみます。
next buildによるビルド実行
$ npm run build
next build
実行時には次のようなログが流れて、ビルドしたページそれぞれにλ
、○
、●
などのマークがついています。ログの下部にも示されているように、これはページのレンダリング方法を示しています。現状の例ではサーバー側のリソースを利用しないただのWebページなので、普通の静的ページを示す○ (Static)
のマークしか現れていません。
next build実行時のログ
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (2/2)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 320 B 63.1 kB
└ ○ /404 3.44 kB 66.2 kB
+ First Load JS shared by all 62.8 kB
├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.c61ba4.js 12.9 kB
├ chunks/framework.ca12ea.js 41.8 kB
├ chunks/main.b9d01f.js 6.29 kB
├ chunks/pages/_app.822f3f.js 1.01 kB
└ chunks/webpack.95c2b2.js 751 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
ビルド結果は.next
ディレクトリに書き出されます。先述したように、これをバージョン管理の対象にする必要はありません。
ビルドができたらnext start
を実行します。
これもpackage.json
には、npm run start
によってnext start
が呼び出されるように設定してあるので、次のように実行します。このとき、サーバーがすでに起動しているとエラーになりますので、サーバーが起動している場合は、いったんcontrol+cキーでサーバーを停止してください。
next startで本番モードの起動
$ npm run start
next dev
のときよりも控えめにログが出力されて、サーバーが起動します。こちらの場合は本番用途なので、コード変更による差分更新やわかりやすいエラー通知といった開発サポート機能は動作しません。
next start実行時のログ
ready - started server on http://localhost:3000
ポート番号を指定した起動ももちろん可能です。
ポート指定を行ったnext startの実行
$ next start -p 3001
Next.jsはそれぞれのページの内容が確定するタイミングがいつなのかを判断して、最適なレンダリング方法を提供します。1つのWebサイトの中で、ビルド時点で内容が確定するページは静的に書き出して、そうでないページはリクエスト時にサーバーから内容を取得するといった働きができます。
後述する静的HTMLの書き出しと違い、公開先にNode.jsサーバーの実行環境は必要になりますが、ページごとに違った柔軟なレンダリング方法をサポートするNext.jsの機能を十分に活用した利用方法です。
Next.jsの静的HTML書き出し
next export
コマンドを使うと、Next.jsで作ったWebサイトをNode.jsサーバーを介してではなく、単純な静的HTMLファイルとして公開できるように書き出すこともできます。事前にnext build
の実行が必要な点についてはnext start
コマンドと同様です。
ビルドは実行済みとして、next export
を実行します。ここではnpm run export
で呼び出せるように設定しているので次のように実行します。
next exportでHTML書き出しの実行
$ npm run export
実行時のログが次の内容になります。.next
の内容を参照してout
ディレクトリに書き出しが行われました。
next export実行時のログ
info - using build directory: /Users/ykhs/sketches/nextjs-example/.next
info - Copying "static build" directory
info - No "exportPathMap" found in "next.config.js". Generating map from "./pages"
info - Launching 7 workers
info - Exporting (2/2)
Export successful. Files written to /Users/ykhs/sketches/nextjs-example/out
out
ディレクトリには次のようにそれぞれのページのHTMLファイルと最適化されたJSファイルなどのアセットが書き出されます。
outディレクトリの内容
out
├── 404.html
├── _next
│ ├── WW134b0dr54Y2ZXchto5h
│ └── static
└── index.html
静的HTMLの書き出しは、Webサイトの公開環境にNode.jsサーバーを必須とせず、すべてのページが事前に書き出し済みであるため、シンプルな仕組みで良好なパフォーマンスを持ったWebサイトを構築できます。
おわりに
今回はSPAやSSR、SSGなどWebページのレンダリング方法の背景も交えつつ、Next.jsの概要とその使い始めにおける知識を紹介しました。次回はNext.jsの特徴である、配置したファイルがそのままWebサイトのパスにになる、ファイルベースのルーティング機能について、もう少し詳しく紹介する予定です。