コンテンツへスキップ
ブログに戻る

2019年9月30日(月)

Next.js 9.0.7

投稿者

Next.js 9.0は、約2ヶ月前にリリースされました。それ以来、私たちは以下の7つの小規模ながらも非常に重要なリリースに取り組んできました。9.0.19.0.29.0.39.0.49.0.59.0.6、そして9.0.7です。

これらのリリースが、破壊的変更なしに、皆様のウェブサイトやアプリケーションにもたらしたものを詳しく見ていきましょう。

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のようなプラットフォームでは、gzipbrotliはプロキシレベルで自動的に処理されます。

セルフホスティングの場合、企業は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にはオンデマンドエントリと呼ばれる概念があります。この機能は、現在作業中のページのみをコンパイルします。

The on-demand entries flow
オンデマンドエントリのフロー

9.0.4以降、Next.jsはオンデマンドエントリによってアクティブにコンパイルされているページにのみ型エラーを報告するようになりました。これにより、特定のページセットに集中しながら、型チェックのノイズを大幅に削減できます。

アプリケーション全体の型チェックは、next build中に引き続き実行されるか、エディターで処理できます。

テレメトリー

Next.jsは約3年前にリリースされ、この3年間で新しい機能からすべてのユーザーにより良いデフォルト設定まで、フレームワークは著しく成長しました。

この改善プロセスへの取り組み方は、非常に手動によるものでした。

Vercelはいくつかの大規模なNext.jsアプリケーションを持っています。例えば、 vercel.comvercel.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モジュールを通じて公開されています。

例えば、ページにタイトルを追加するには

pages/index.js
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への貢献が容易になります。単一のコンパイルプロセスと統合されたビルド設定が利用できるようになりました。以前は、nextnext-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倍になりました。ぜひご参加ください!

私たちは、継続的なコミュニティ貢献、そしてリリースの形成に役立つ企業やユーザーからの外部フィードバックに感謝しています。