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/dynamic
など、Next.jsの特定の部分が使用されていない場合、それらはランタイムで不要になり、バンドルサイズが小さくなります。
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圧縮
企業がカスタムサーバーを使用する理由を調査している際、最も多い理由が圧縮であることがわかりました。企業はcompression
というExpressミドルウェアを追加し、HTTPレスポンスのGzip圧縮を処理していました。
このミドルウェアはレスポンスを圧縮するため、ユーザーに送信されるバイト数が減少します。通常、これはNginxのようなリバースプロキシで処理されるべきです。リバースプロキシはCPU負荷の高い作業をシングルスレッドのNodeプロセスから取り除きます。
しかし、ウェブ上のNext.jsの使用状況を調査したところ、多くの企業が圧縮を全く設定していないことがわかりました。
Vercelのようなプラットフォームでは、gzip
とbrotli
はプロキシレベルで自動的に処理されます。
セルフホスティングの場合、企業はcompression
またはリバースプロキシを介してgzipを自分で追加する必要があります。
Next.js 9.0.4から、next start
またはカスタムserver.js
を使用する場合、gzip
圧縮がデフォルトで含まれるようになりました。
brotli
サポートは近日中に追加される予定です。Node.jsがネイティブで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.orgです。私たちはVercel内部でNext.jsをドッグフーディングし、経験に基づいてNext.jsを改善してきました。
それに加えて、私たちは積極的にコミュニティと関わり、フィードバックを収集しています。あなたも以前にTimと話して、あなたの会社でNext.jsをどのように使用しているかについてフィードバックを提供したことがあるかもしれません。
例えば、カスタムサーバーを使用しているか、カスタムwebpack設定を持っているかなどです。このフィードバックはNext.jsの機能開発を導く上で非常に貴重です。
しかし、このアプローチには問題があります。それは、私たちが一部のユーザーからのみフィードバックを収集できるということです。このサブセットは、あなたやあなたの会社とは異なるニーズやユースケースを持っているかもしれません。
この一例がCSSファイルのインポートです。これは非標準ですが、next-css
(またはSass/Less)、またはカスタム設定を介して、かなりの数のユーザーがこれを使用しているようです。その特定のアプローチを使用しているユーザーの割合がわかれば、それを改善する優先順位を決めることができます。
このため、私たちはこれらのフィードバックポイントを収集するための匿名のより自動化されたアプローチを導入しました。これにより、近い将来、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は追加の<meta>
タグを挿入し、next/head
がレンダリングした要素の数を保持します。これにより、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フレームワークは多くの部分で構成されています。その1つがクライアントサイドランタイムです。このランタイムはハイドレーション、クライアントサイドルーティングなどを処理します。
この改善が焦点を当てたハイドレーションは、サーバーサイドレンダリングまたはプリレンダリングされた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をアプリに実装している場合は、AppTree
アプローチに更新することをお勧めします。これにより、useRouter
や将来追加される他のAPIがNext.jsアプリケーションで正しく動作するようになります。
Apolloや類似のライブラリを使用していない場合は、一般的にReactツリーを走査することを避けることをお勧めします。Next.jsチームは一般的にReactツリーの走査を推奨していません。なぜなら、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へのアップグレードを推奨します。
ほとんどの場合、アップグレードに変更は必要ありません。アップグレードガイドに従って、スムーズなアップグレード体験を確保できます。
このガイドの公開以来、更新に貢献してくださったすべてのコミュニティコントリビューターに感謝いたします。
今後の予定
このブログ記事で概説した新しい最適化は、私たちが取り組んできたより広範な最適化と機能の始まりに過ぎません。
進行中のRFCに関する最新情報をすぐに共有する予定です。それまでは、GitHubのRFCラベルを通じて少しだけ先取りできます。
これには、組み込みCSSサポート、publicディレクトリサポート、srcディレクトリサポートなど、私たちが調査してきた機能の一部が示されています。
コミュニティ
Next.jsコミュニティの継続的な成長を嬉しく思っています。
- 800人以上のコントリビューターが少なくとも1つのコミットを実施
- GitHubでプロジェクトが41,100回以上スター獲得
Next.jsコミュニティは前回のメジャーリリース以降倍増し、10,900人以上のメンバーが参加しています。ぜひ参加してください!
リリースを形作る企業やユーザーからの外部フィードバックとコミュニティ貢献の継続に興奮しています。