2019年9月30日(月)
Next.js 9.0.7
投稿者Next.js 9.0は、約2ヶ月前にリリースされました。それ以来、私たちは以下の7つの小規模ながらも非常に重要なリリースに取り組んできました。9.0.1、9.0.2、9.0.3、9.0.4、9.0.5、9.0.6、そして9.0.7です。
これらのリリースが、破壊的変更なしに、皆様のウェブサイトやアプリケーションにもたらしたものを詳しく見ていきましょう。
- Windows環境における並行処理の改善:
next build
プロセスはWindowsでより信頼性が高まり、より優れた並行処理が可能になりました。 - デフォルトでのGzip圧縮: 最適化の手順を減らすため、Gzip圧縮がデフォルトで追加されるようになりました。
- アクティブなページのみに限定されたTypeScriptレポート: 組み込みのTypeScriptサポートが拡張され、アクティブなページの変更のみを監視するようになり、より高速で信頼性が向上しました。
- テレメトリー: これにより、Next.jsのどの部分を最適化すべきかに注力し、最適化が意図した効果をもたらしているかを検証するのに役立ちます。
next/head
要素の追跡機能の改善:next-head
クラスが削除され、実装を検証する特定のツールをより簡単に導入できるようになりました。- pagesディレクトリ内での非ページファイルの防止: 誤って非ページファイルを公開してしまうのを防ぐことで、アプリケーションのセキュリティと
next build
時間を最適化します。 - ランタイムの改善: Next.jsの特定の部分(例:
next/dynamic
)が使用されない場合、それらはランタイム時に不要となり、バンドルサイズが小さくなります。
Windows環境における並行処理の改善
Next.jsは、next build
プロセス中に多くの箇所で並行処理を行います。主な用途は、Terserを用いてビルド出力を並行して最小化することです。
以前は、この処理はworker-farm
と呼ばれるパッケージを使用して、多数のCPUで実行されていました。しかし、多くのWindowsユーザーがカスタムのwebpack設定でミニファイを無効にしていることに気づきました。さらに調査したところ、worker-farm
がWindowsマシンで一貫して動作しないことが判明しました。
この問題を解決するため、worker-farm
からjest-worker
に移行しました。これにより、macOS、Linux、Windowsのいずれのマシンでも、ビルドが信頼性が高く一貫性のあるものになります。
jest-worker
は、その名の通りJestテストランナーの一部です。Jestがテストケースを並行処理するために使用するパッケージです。つまり、このパッケージは非常に実証済みで、信頼性が高く、メンテナンスも継続されています。
jest-worker
は、Node 12の新機能であるworker_threads
もサポートしています。child_process
とは異なり、worker_threads
はメモリを共有できるため、新しいNodeバージョンでのビルド時間が高速になります。
jest-worker
に切り替えることで、Windowsユーザー向けにビルドの並行処理を再度有効にすることができました。
デフォルトでのGzip圧縮
企業がカスタムサーバーを使用する理由を調査したところ、ほとんどの場合、圧縮のためであることが判明しました。企業は、HTTPレスポンスのGzip圧縮を処理するcompression
と呼ばれるExpressミドルウェアを追加していました。
このミドルウェアはレスポンスを圧縮し、ユーザーに送信されるデータ量を削減します。通常、これはNginxのようなリバースプロキシによって処理されるべきです。リバースプロキシは、シングルスレッドのNodeプロセスからCPU負荷の高い作業を取り除きます。
しかし、Web上でのNext.jsの使用状況を調査したところ、かなりの割合の企業が何の圧縮も設定していないことが判明しました。
Vercelのようなプラットフォームでは、gzip
とbrotli
はプロキシレベルで自動的に処理されます。
セルフホスティングの場合、企業はgzip(compression
またはリバースプロキシ経由)を自分で追加する必要があります。
Next.js 9.0.4以降、next start
またはカスタムのserver.js
を使用する場合、gzip
圧縮がデフォルトで含まれるようになりました。
Node.jsがBrotliをネイティブでサポートするようになったため、brotli
のサポートも間もなく提供されます。
アプリケーションでカスタムサーバー経由で圧縮がすでに有効になっている場合、Next.jsは独自のコンプレッサーを追加しません。
Next.jsは、サーバーレスターゲット向けにデフォルトで圧縮を含みません。これは、サーバーレスターゲットを使用する場合、アセットが個別にアップロードされ、Node.jsを介して提供されないためです。
Vercelなどの圧縮を処理するプラットフォームにデプロイしている場合、変更は不要です。
アクティブなページのみに限定されたTypeScriptレポート
Next.js 9にはTypeScriptの組み込みサポートが含まれています。必要なのは、単一のページ名を.js
から.tsx
に変更することだけです。Next.jsが残りの設定を自動的に処理するか、ガイドします。
Next.jsは、開発プロセスと並行してtsc --watch
を実行することで型チェックも処理します。開発中、Next.jsにはオンデマンドエントリと呼ばれる概念があります。この機能は、現在作業中のページのみをコンパイルします。

9.0.4以降、Next.jsはオンデマンドエントリによってアクティブにコンパイルされているページにのみ型エラーを報告するようになりました。これにより、特定のページセットに集中しながら、型チェックのノイズを大幅に削減できます。
アプリケーション全体の型チェックは、next build
中に引き続き実行されるか、エディターで処理できます。
テレメトリー
Next.jsは約3年前にリリースされ、この3年間で新しい機能からすべてのユーザーにより良いデフォルト設定まで、フレームワークは著しく成長しました。
この改善プロセスへの取り組み方は、非常に手動によるものでした。
Vercelはいくつかの大規模なNext.jsアプリケーションを持っています。例えば、 vercel.com、vercel.com/docs、そしてhttps://nextjs.dokyumento.jpなどです。私たちはVercel社内でNext.jsを ドッグフーディングし、その経験に基づいてNext.jsを改善してきました。
それに加えて、私たちは積極的にコミュニティと交流し、フィードバックを収集しています。おそらく、あなたは以前Timと話して、会社でどのようにNext.jsを使っているかについてフィードバックを提供したことがあるでしょう。
例えば、カスタムサーバーを使用しているか、カスタムのwebpack設定を持っているか、などです。このフィードバックは、Next.jsの機能開発の方向性を決める上で非常に貴重です。
しかし、このアプローチには問題があります。それは、ユーザーの一部からしかフィードバックを収集できないことです。この一部のユーザーは、あなた/あなたの会社とは異なるニーズや使用事例を持っている可能性があります。
この一例として、標準的ではないものの、多くのユーザーが next-css
(またはSass/Less)やカスタム設定を通じて使用しているCSSファイルのインポートが挙げられます。その特定のアプローチを使用しているユーザーの割合が分かれば、改善の優先順位を付けることができます。
このため、私たちはこれらのフィードバックを収集するための匿名でより自動化されたアプローチを導入しました。これにより、近い将来さらにNext.jsを改善することができます。
これにより、フレームワークに行われた改善が、すべてのアプリケーションのベースラインを向上させているかどうかを検証することも可能になります。
テレメトリーの詳細については、nextjs.org/telemetryをご覧ください。参加を希望しない場合は、オプトアウトする方法もそこで見つけることができます。
アクティビティを示すドット付きビルドフィードバック
Next.jsのユーザーと話している中で、next build
が何もしていないように見えることがある、という小さなフィードバックがありました(主に視覚的な面で)。
これを解決するため、next build
の実行中にコンソール出力にローディングインジケーターを追加しました。この出力は、コマンドがまだ実行中であり、プロセスがフリーズしていないことを視覚的に示します。
可能であれば、このビルド出力を拡張して、ビルドのより多くの段階を表示する予定です。
next/head
要素の追跡機能の改善
Next.jsは、アプリケーションのHTMLレンダリングを担当するため、<head>
要素を管理するための組み込みの方法を提供しています。このAPIは、next/head
モジュールを通じて公開されています。
例えば、ページにタイトルを追加するには
import Head from 'next/head';
export default function MyPage() {
return (
<>
<Head>
<title>My Title</title>
</Head>
<h1>Hello World</h1>
</>
);
}
HTMLにレンダリングする際、Next.jsは<Head>
内でレンダリングされたすべてのコンポーネントを収集し、それらのタグをページの<head>
内にレンダリングします。
しかし、Next.jsは<Link>
コンポーネントを使用してシングルページアプリケーション(SPA)型のルーティング遷移を可能にします。
<Link>
をクリックすると、Next.jsはクライアント側でページをレンダリングするために必要なJavaScriptファイルをフェッチします。その後、そのファイルに関連付けられたReactコンポーネントをレンダリングします。
この遷移はクライアント側で行われるため、前のページから挿入された<head>
要素をクリーンアップする必要があります。そうしないと、古い要素が別のページに残ってしまう可能性があります。
以前、Next.jsは<Head>
によって提供されるすべての要素にクラス名を追加することで、これらの要素を追跡していました。
上記の例を挙げると、<head>
は以下のようになります。
<head>
<title class="next-head">My Title</title>
</head>
next/head
を通じて挿入されたすべての要素が明確にマークされ、簡単にクリーンアップできたため、この解決策はうまく機能していました。
しかし、一部のユーザーから、要素に付加される余分なclass
属性が原因で、外部サービスから追加されるスクリプトが検証されないことがあるという問題が報告されました。
Google ChromeチームのGerald Monaco氏が、要素にクラス名がなくてもクリーンアップ動作を維持する方法を考案しました。
新しい動作では、Next.jsは(next/head
が)レンダリングした要素の数を保持する追加の<meta>
タグを挿入します。これにより、Next.jsはその数を使用して既存の要素をクリーンアップできます。
結果として、このアプローチは、複数の要素がページの<head>
に挿入される際の初期HTMLペイロードサイズを削減します。
pagesディレクトリ内での非ページファイルの防止
Next.jsを使い始める際、最初にすることはpages
ディレクトリを作成することです。
慣例として、pages
ディレクトリ内のすべてのファイルがアプリケーションのルートになります。簡単な例としては、pages/about.js
が/about
になることです。
時が経つにつれて、大規模・小規模を問わず、ユーザーのアプリケーションのビルドパフォーマンスが悪いという報告が時々寄せられるようになりました。
さらに調査した結果、これらのユーザーはコンポーネント構造全体をpages
ディレクトリ内に作成していることが判明しました。
pages
ディレクトリ内のすべてのファイルがページとして扱われるため、すべてのコンポーネントがアプリケーション内でページとしてコンパイルされていました。これにより、大量のビルド時オーバーヘッドが発生し、無効なページのために2つ以上のJavaScriptファイルが生成されていました。
さらに、これはNext.jsがコード分割チャンクを生成する方法に部分的に影響を与えました。これは、Next.jsがページ間のライブラリ使用状況に関するヒューリスティックを使用しているためです。
このため、ユーザーがNext.jsアプリケーションにこの落とし穴を持ち込まないようにする必要があります。
Next.js 9は、pages
ディレクトリ内のファイルがReactコンポーネントをエクスポートしていることを検証するようになりました。
これにより、Next.jsはpages
ディレクトリ内に潜在的な非ページファイルが見つかったことを警告するメッセージを表示します。
これにより、ユーザーはページではないファイルを別のディレクトリに移動させることが促されます。その結果、開発、プロダクション、コード分割がより高速かつ正確になります。
ランタイムの改善
Next.jsフレームワークは多くの部分から構成されています。その一つがクライアントサイドのランタイムです。このランタイムは、ハイドレーション、クライアントサイドルーティングなどを処理します。
この改善が焦点を当てたハイドレーションは、サーバーでレンダリングされた、またはプリレンダリングされたHTMLをインタラクティブにするために必要なものです。ハイドレーションはイベントハンドラを追加し、useEffect()
やcomponentDidMount
のようなライフサイクルメソッドを呼び出し、アプリケーションをエンドユーザーが使える状態にします。
さらに、Next.jsは基本的なハイドレーションだけでなく、例えばクライアントサイドのルーターのセットアップ、next/head
の設定、そしてnext/dynamic
を介した追加のアプリケーションロジックのロードなども処理します。
これらの各責任には、それぞれ独自のランタイム部分も存在します。
next/dynamic
の場合、Next.jsはサーバーでレンダリングされた遅延ロードコンポーネントがクライアント側で準備されていることを確認する必要があります。next/dynamic
を使用するたびに、追加のJavaScriptバンドルが生成され、ハイドレーションの不一致を避けるためにこれらのファイルはハイドレーション前にロードされる必要があります。
以前は、このランタイムは常にNext.jsのランタイムバンドルに含まれていました。現在では、アプリケーションでnext/dynamic
を使用する場合にのみ含まれるようになりました。これにより、next/dynamic
を使用しないアプリケーションでは、ダウンロード、パース、実行されるJavaScriptが少なくなります。
AppTreeのサポート
Reactエコシステムの一部のライブラリは、非常に特定のやり方でサーバーサイドレンダリングを実装しています。最も顕著なのは、ApolloのサーバーサイドレンダリングソリューションであるgetDataFromTree
で、Reactツリーをレンダリングし、見つかったすべてのQuery
の結果を待ってから、再度Reactツリーをレンダリングすることで動作します。
デフォルトでは、Next.jsはReactツリーにいくつかのコンテキスト値を追加します。例えば、useRouter
を使用して読み取ることができるルーターなどです。
with-apollo
の例でReactツリーをレンダリングする方法は、<App>
をレンダリングし、不足しているプロパティを手動で埋め込もうとするものでした。React Contextが追加されたことで、コンテキストプロバイダーが別の要素となったため、これはもはや不可能になりました。
Next.js 9.0.4以降、getInitialProps
のコンテキストオブジェクトにAppTree
という新しいプロパティが追加されました。これは、ApolloのgetDataFromTree
のように、外部ライブラリがReactツリー全体を走査する必要があるケースのために特別に追加されました。
with-apollo
の例は、変更を反映するために更新されました。すでにアプリケーションにApolloを実装している場合、useRouter
や将来追加されるその他のAPIがNext.jsアプリケーションで正しく動作するように、AppTree
のアプローチに更新することをお勧めします。
Apolloや類似のライブラリを使用していない場合は、Next.jsチームは一般的にReactツリーを走査することを推奨していないため、AppTreeの使用を避けるように試みることをお勧めします。これは、Reactツリーが一度だけでなく複数回レンダリングされるため、かなりのパフォーマンスオーバーヘッドを追加します。
next-server
パッケージの削除
1年以上前にNext.jsをサーバーレスデプロイ用に最適化し始めた際、next-server
というパッケージを作成しました。このパッケージは実験的なもので、next
パッケージと一緒に公開されました。公式にはドキュメント化されていませんでしたが、可能な限り最小のNext.jsサーバーランタイムを作成するための実験でした。
最終的に、このパッケージは成功し、本番サーバーのランタイムを小型化することができました。しかし、Next.jsコンパイラと静的解析により、ランタイムをさらに小型化する革新的な新しい方法を考案しました。
そうすることで、next-server
は廃止され、サーバーレスターゲットに置き換えられました。このターゲットは、next-server
パッケージをnext
の代替として使用するよりもはるかに最適化された出力を提供します。
このパッケージは廃止され、直接使用することはできませんでしたが、私たちはそれを保持していました。これは、パッケージ間で共有される内部コードがあり、そのコードを移動するにはかなりの労力が必要だったためです。
最近、私たちはこの努力を行い、next-server
からnext
パッケージにコードを戻しました。これにより、Next.jsフレームワークのすべてのコードがnext
パッケージ内に存在することになります。
これにより、初心者から経験豊富なコントリビューターまで、Next.jsへの貢献が容易になります。単一のコンパイルプロセスと統合されたビルド設定が利用できるようになりました。以前は、next
とnext-server
で個別の設定があり、各パッケージにどのコードが属するかについて恣意的な制約がありました。
Next.jsのアップグレード
プロジェクトが以前のバージョンのNext.jsで動作している場合は、Next.js 9へのアップグレードをお勧めします。
ほとんどの場合、アップグレードに際して変更は不要です。スムーズなアップグレード体験を確実にするため、アップグレードガイドに従ってください。
リリース以来ガイドを更新してくださったすべてのコミュニティ貢献者に感謝いたします。
今後の予定
このブログ記事で概説されている新しい最適化は、私たちが取り組んでいるより広範な最適化と機能の始まりに過ぎません。
進行中のRFCsに関するアップデートを間もなく共有します。それまでは、GitHubのRFCラベルを通じて小さな先行公開を見ることができます。
これは、組み込みのCSSサポート、publicディレクトリサポート、そしてsrcディレクトリサポートなど、私たちが調査しているいくつかの機能を示しています。
コミュニティ
Next.jsコミュニティの継続的な成長を嬉しく思います。
- これまでに800人以上の貢献者が少なくとも1つのコミットを行いました。
- GitHubでは、プロジェクトに41,100回以上のスターが付けられました。
Next.jsコミュニティは、前回のメジャーリリース以降、メンバー数が10,900人を超え、2倍になりました。ぜひご参加ください!
私たちは、継続的なコミュニティ貢献、そしてリリースの形成に役立つ企業やユーザーからの外部フィードバックに感謝しています。