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

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負荷の高い作業を削除します。

しかし、ウェブ上での 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/docshttps://nextjs.dokyumento.jpです。私たちはVercel社内でNext.jsをドッグフーディングしており、その経験に基づいてNext.jsを改善してきました。

さらに、コミュニティと積極的に関わり、フィードバックを集めています。あなたは以前に、あなたの会社でどのようにNext.jsを使用しているかについて、Timと話したことがあるかもしれません。

例えば、カスタムサーバーを使用している場合、カスタムwebpack構成がある場合などです。このフィードバックは、Next.jsの機能開発を推進する上で非常に貴重です。

ただし、このアプローチには問題があり、それは一部のユーザーからしかフィードバックを収集できないということです。このサブセットは、あなた/あなたの会社とは異なるニーズやユースケースを持っている可能性があります。

この例の1つとして、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モジュールを介して公開されます。

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

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フレームワークは、多くの部分で構成されています。その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は、useRouterを使用して読み取ることができるルーターなど、いくつかのコンテキスト値をReactツリーに追加します。

with-apolloの例でReactツリーをレンダリングする方法は、<App>をレンダリングし、不足しているプロパティを手動で埋めようとすることでした。Reactコンテキストの追加により、コンテキストプロバイダーは別個の要素であるため、これはもはや不可能になりました。

Next.js 9.0.4以降では、getInitialPropsのコンテキストオブジェクトにAppTreeと呼ばれる新しいプロパティが追加されました。これは、ApolloのgetDataFromTreeのように外部ライブラリがReactツリー全体を走査する必要がある場合のために特別に追加されました。

with-apolloの例は、変更を反映するように更新されました。すでにアプリケーションにApolloを実装している場合は、AppTreeのアプローチに更新して、useRouterおよび将来追加されるその他のAPIがNext.jsアプリケーションで正しく動作するようにすることをお勧めします。

Apolloまたは同様のライブラリを使用していない場合は、AppTreeの使用を避けることをお勧めします。Next.jsチームは、一般的にReactツリーの走査を推奨していないためです。Reactツリーは1回だけでなく複数回レンダリングされるため、パフォーマンスのオーバーヘッドがかなり増加します。

next-serverパッケージの削除

1年以上前に、Next.jsをサーバーレスデプロイ向けに最適化し始めたとき、next-serverというパッケージを作成しました。このパッケージは実験的なものであり、nextパッケージと一緒に公開されました。これは、最小限のNext.jsサーバーランタイムを作成するための実験であり、一般に公開されたドキュメントはありませんでした。

最終的に、このパッケージは成功し、本番サーバーのランタイムが小さくなりました。しかし、Next.jsコンパイラーと静的分析により、ランタイムをさらに小さくする革新的な新しい方法を思いつきました。

これにより、next-serverは廃止され、サーバーレスターゲットに置き換えられました。このターゲットは、nextの代替としてnext-serverパッケージを使用するよりも、はるかに最適化された出力を持ちます。

このパッケージは廃止されており、直接使用することはできませんでしたが、維持されていました。これは、パッケージ間で共有されている内部構造があり、コードを移動するにはかなりの労力がかかるためでした。

最近、この作業を行い、next-serverのコードをnextパッケージに戻しました。つまり、Next.jsフレームワークのすべてのコードはnextパッケージに存在することになります。

これにより、初心者も経験豊富な貢献者もNext.jsに貢献しやすくなります。単一のコンパイルプロセスと統合されたビルド構成があります。以前は、nextnext-serverに個別の設定があり、どのコードが各パッケージに属するかについて恣意的な制約がありました。

Next.jsのアップグレード

プロジェクトが古いバージョンのNext.jsで実行されている場合は、Next.js 9にアップグレードすることをお勧めします。

ほとんどの場合、アップグレードに必要な変更はありません。アップグレードガイドに従って、スムーズなアップグレードを実現してください。

リリース以来、ガイドを更新してくれたすべてのコミュニティ貢献者に感謝いたします。

今後の展望

このブログ記事で概説した新しい最適化は、私たちが取り組んできたより広範な最適化と機能のほんの始まりに過ぎません。

近日中に、進行中のRFCに関する最新情報をお知らせします。それまでは、GitHubのRFCラベルを通じて、ちょっとしたプレビューをご覧いただけます。

これは、私たちが調査してきた機能の一部を示しています。例えば、組み込みCSSサポートpublicディレクトリのサポート、そしてsrcディレクトリのサポートなどがあります。

コミュニティ

Next.jsコミュニティの継続的な成長を嬉しく思っています。

  • 少なくとも1つのコミットを完了させたコントリビューターは800人以上です。
  • GitHubでは、このプロジェクトは41,100回以上スターされています。

Next.jsコミュニティは前回のメジャーリリースから倍増し、10,900人以上のメンバーがいます。ぜひご参加ください!

コミュニティからの継続的な貢献と、リリースを形作るのに役立つ企業やユーザーからの外部フィードバックに大変感謝しています。