これから始める、Next.js 第1回 Next.jsとは

Next.jsの特徴を簡単なデモを使って確認してみます。Reactをベースとし、用途に合わせSSRやSSGを組み合わせた柔軟なサイト書き出し機能を試してみましょう。

発行

著者 山田 順久 フロントエンド・エンジニア
これから始める、Next.js シリーズの記事一覧

はじめに

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からnextreactreact-domの3つのパッケージをインストールします。

必要なnpmパッケージのインストール

$ npm install next react react-dom

執筆時点でインストールされるパッケージのバージョンは、それぞれ次のとおりです。

  • next:10.0.5
  • react:17.0.1
  • react-dom:17.0.1

インストールができたら、よく使うコマンド*をpackage.jsonscriptsに設定しておきます。

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 buildnext 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.jsonscriptsを設定してあるので、これによってnext devコマンドが実行されます。

$ npm run dev

npm-scriptsの使い方

package.jsonscriptsプロパティには、好きなコマンドの呼び出しを設定しておくことができます。

Next.jsのnpmパッケージであるnextをインストールすると、nextコマンドも使えるようになります。その本体は./node_modules/.bin/nextにあります。すると、これを呼び出すには次のようなコマンドになるわけですが、これは少し長いです。

$ ./node_modules/.bin/next dev

そこで、次のようにpackage.jsonscriptsnext 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サイトのパスにになる、ファイルベースのルーティング機能について、もう少し詳しく紹介する予定です。