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

2020年7月27日(月)

Next.js 9.5

投稿者

本日、Next.js 9.5を発表できることを嬉しく思います。主な機能は以下のとおりです。

安定したインクリメンタル静的再生成

Next.jsは9.3で静的サイト生成メソッドを導入しました。明確な目標を持っていました。それは静的な利点(常に高速、常にオンライン、グローバルに複製)を得つつ、Next.jsが得意とする動的なデータに対する優れたサポートも得ることでした。

両方の長所を得るために、Next.jsはインクリメンタル静的生成を導入しました。これにより、サイトのビルド後に静的なコンテンツを更新できます。getStaticPathsfallback: trueオプションを使用すると、新しい静的ページ実行時に登録できます。

Next.jsは、データセットのサイズに関係なく、オンデマンドでこの方法で無限の数のページを静的にプリレンダリングできます。

本日、トラフィックが入ってきたときにバックグラウンドで再レンダリングすることにより、既存のページを更新するメカニズムであるインクリメンタル静的再生成一般提供を発表します。

stale-while-revalidateに触発された、バックグラウンド再生成は、常に静的ストレージから、中断することなくトラフィックが提供されるようにし、新しく構築されたページは、生成が完了した後にのみプッシュされます。

export async function getStaticProps() {
  return {
    props: await getDataFromCMS(),
    // we will attempt to re-generate the page:
    // - when a request comes in
    // - at most once every second
    revalidate: 1,
  };
}

revalidateフラグは、https://en.wikipedia.org/wiki/Cache_stampedeを防ぐために、最大1回の生成が行われる秒数です。

従来のSSRとは異なり、インクリメンタル静的再生成を使用すると、静的なメリットを維持できます。

  • レイテンシーのスパイクはありません。ページは常に高速に提供されます。
  • ページがオフラインになることはありません。バックグラウンドでのページ再生成に失敗した場合、古いページは変更されないままになります。
  • データベースとバックエンドの負荷が低い。ページは最大で1回のみ`concurrently`再計算されます。

インクリメンタルな機能(ページの追加と遅延更新)と、プレビューモードの両方が安定し、next startVercel Edgeプラットフォームですぐに完全にサポートされるようになりました。

この新機能を実証するために、特定のIssueに対するGitHubの様々なリアクション数を表示する静的ページを再生成する例を作成しました。こちらをご覧ください: https://reactions-demo.vercel.app/

After the first visit following our emoji reaction, a new page generation kicks off in the background. Every single request throughout is served from static cache.
絵文字リアクション後の最初のアクセス後、バックグラウンドで新しいページ生成が開始されます。その間、すべてのリクエストは静的キャッシュから提供されます。

次に、インクリメンタルな静的生成機能に関する2つの追加機能に対応するための補足的なRFCに取り組む予定です。

  • 一度に複数のページ(ブログのインデックスや特定のブログ記事など)を再生成および無効化する
  • ユーザーのトラフィックに先立ち、イベント(CMSのWebhookなど)をリスニングして再生成する

詳細については、getStaticPropsのドキュメントをご覧ください。

カスタマイズ可能なベースパス

Next.jsプロジェクトは必ずしもドメインのルートから提供されるとは限りません。Next.jsプロジェクトがドメインのそのサブセクションのみをカバーするように、/docsのようなサブパスの下にNext.jsプロジェクトをホストしたい場合があります。

これまでもこれは可能でしたが、かなりの追加設定が必要でした。例えば、すべての<Link>にプレフィックスを追加し、Next.jsが正しいパスからJavaScriptバンドルを提供していることを確認する必要がありました。

この問題を解決するために、新しい設定オプションを導入します。basePathを使用すると、ドメインのサブパスにNext.jsプロジェクトを簡単にホストできます。

basePathの使用を開始するには、next.config.jsに追加します。

next.config.js
module.exports = {
  basePath: '/docs',
};

basePathを設定すると、プロジェクトは提供されたパス(この場合は/docs)から自動的にルーティングされます。

next/linkまたはnext/routerを使用してプロジェクト内の他のページにリンクすると、basePathが自動的にプレフィックスとして付加されます。これにより、プロジェクトを変更せずにbasePathを変更できます。

この例として、next/linkを使用して別のページにルーティングする方法があります。

import Link from 'next/link';
 
export default function HomePage() {
  return (
    <>
      <Link href="/documentation-page">
        <a>Documentation page</a>
      </Link>
    </>
  );
}

このようにnext/linkを使用すると、Webブラウザに次のHTMLがレンダリングされます。

<a href="/docs/documentation-page">Documentation page</a>

詳細については、basePathのドキュメントをご覧ください。

リライト、リダイレクト、ヘッダーのサポート

リライト

Next.jsプロジェクトを構築する際に、特定のルートを別のURLにプロキシしたい場合があります。例えば、Next.jsを段階的にスタックに採用する場合、Next.jsプロジェクトに存在するページをルーティングし、一致しなかったすべてのものを移行元の古いプロジェクトにルーティングする必要があります。

Next.js 9.5では、新しい設定オプションrewritesを導入します。これを使用すると、受信リクエストパスを、外部URLを含む別の宛先パスにマッピングできます。

例えば、特定のルートをexample.comにリライトしたい場合があります。

next.config.js
module.exports = {
  async rewrites() {
    return [
      { source: '/backend/:path*', destination: 'https://example.com/:path*' },
    ];
  },
};

この場合、/backend下のすべてのパスがexample.comにルーティングされます。

また、Next.jsプロジェクトのルートが一致するかどうかを確認し、一致するものがない場合は以前のプロジェクトにリライトすることもできます。これは、Next.jsの段階的な採用に非常に役立ちます。

module.exports = {
  async rewrites() {
    return [
      // check if Next.js project routes match before we attempt proxying
      {
        source: '/:path*',
        destination: '/:path*',
      },
      {
        source: '/:path*',
        destination: `https://example.com/:path*`,
      },
    ];
  },
};

この場合、最初にすべてのパスを照合します。一致するものがなければ、以前のプロジェクトであるexample.comにプロキシします。

rewrites機能の詳細については、リライトに関するドキュメントをご覧ください。

リダイレクト

ほとんどのWebサイトでは、少なくともいくつかのリダイレクトが必要です。特に、プロジェクトルートの構造を変更する場合(例:/blog/newsに移動する場合など)に必要になります。

以前は、Next.jsプロジェクトでリダイレクトのリストを持つには、カスタムサーバーまたはカスタム_errorページを設定して、ルートにリダイレクトが設定されているかどうかを確認する必要がありました。ただし、これには、主要な静的およびサーバーレスの最適化を無効にする(サーバーを持つことによって)という犠牲が伴うか、または十分に人間工学的ではありませんでした。

Next.js 9.5以降では、next.config.jsredirectsキーの下にリダイレクトのリストを作成できるようになりました。

next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
    ];
  },
};

redirects機能の詳細については、リダイレクトに関するドキュメントをご覧ください。

ヘッダー

Next.jsを使用すると、静的生成とサーバーサイドレンダリングの両方を使用するハイブリッドプロジェクトを構築できます。サーバーサイドレンダリングを使用すると、受信リクエストのヘッダーを設定できます。静的ページの場合、これまでヘッダーを設定することはできませんでした。

next.config.jsに、すべてのNext.jsルートに適用されるheadersプロパティが導入されました。

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Feature-Policy',
            // Disable microphone and geolocation
            value: "microphone 'none'; geolocation 'none'",
          },
        ],
      },
    ];
  },
};

headersオプションを使用すると、Feature-PolicyContent-Security-Policyのような、一般的に必要なヘッダーを設定できます。

headers機能の詳細については、ヘッダーに関するドキュメントをご覧ください。

URLの末尾のスラッシュはオプション

Next.jsが3年前に導入されたとき、デフォルトの動作では、末尾にスラッシュが付いたすべてのURLは常に404ページを返していました。

有効ではありましたが、一部のユーザーは、この動作を変更する機能を要求していました。例えば、以前は常に末尾にスラッシュが強制されていた既存のプロジェクトをNext.jsに移行する場合などです。

Next.js 9.5では、next.config.jstrailingSlashという新しいオプションが導入されました。

この新しいオプションにより、Next.jsが末尾のスラッシュの動作を自動的に処理するようになります。

  • 例えば、末尾にスラッシュが付いたURLを末尾にスラッシュがないURL(例:/about/から/about)に自動的にリダイレクトします。
  • trailingSlashtrueに設定されている場合、末尾にスラッシュがないURLは末尾にスラッシュが付いたURL(例:/aboutから/about/)にリダイレクトされます。
  • next/linkに末尾のスラッシュが自動的に適用/削除され、不必要なリダイレクトを回避します。
next.config.js
module.exports = {
  // Force a trailing slash, the default value is no trailing slash (false)
  trailingSlash: true,
};

trailingSlash機能の詳細については、trailingSlashに関するドキュメントをご覧ください。

ページバンドルの永続的なキャッシュ

Next.jsページを作成する場合、すべてのスクリプトバンドル、CSSスタイルシート、HTMLの作成は完全に自動化されており、抽象化されています。Next.js 9.5より前の生成された<script>タグを調べると、そのURLが次のようなパターンに従っていることに気付くでしょう。

/_next/static/ovgxWYrvKyjnlM15qtz7h/pages/about.js

上記のパスセグメントovgxWYrvKyjnlM15qtz7hは、ビルドIDと呼ばれていたものです。これらのファイルは、エッジやユーザーのコンピューターで簡単にキャッシュできましたが、アプリを再ビルドすると、ビルドIDが変更され、すべてのキャッシュが破棄されていました。

ほとんどのプロジェクトではこのトレードオフで問題ありませんでしたが、変更されていないページのブラウザキャッシュを無効にしないことで、この動作をさらに最適化したいと考えました。

Google Chromeチームとの共同開発で開発されたNext.js 9.2でのコード分割戦略の改善の導入により、Next.jsのページバンドル生成に対するこれらの改善の土台が築かれました。

Next.js 9.5 以降では、すべてのページの JavaScript バンドルはビルド ID の代わりにコンテンツハッシュを使用するようになります。これにより、デプロイ間で変更されていないページは、再度ダウンロードする必要なくブラウザとエッジキャッシュに残すことができます。

対照的に、これらの変更後の URL パターンは次のようになります。

#pages/about.qzfS4o5gIEXRME6sTEahL.js

グローバルなビルド ID の代わりに、qzfS4o5gIEXRME6sTEahL の部分は、about.js バンドルの決定論的なハッシュであり、サイトのその部分のコードが変更されない限り安定します。さらに、Next.js が自動的に設定する Cache-Control: public,max-age=31536000,immutable を介して、再デプロイ全体で長期的にキャッシュされるようになりました

Fast Refresh の機能強化

Next.js 9.4 でFast Refresh を導入しました。これは、React コンポーネントへの編集に対するフィードバックを即座に提供する新しいホットリロードエクスペリエンスです。

Next.js 9.5 では、Fast Refresh の実装がさらに改良され、成功に必要なツールが提供されます。

  • 理解しやすいエラー: すべてのコンパイルエラーとランタイムエラーが更新され、エラーを引き起こしたコードのコードフレームを含む、関連情報のみが表示されるようになりました
  • 開発時のコンポーネント状態を保持するためのヒント: Next.js は、可能な限り多くのシナリオで Fast Refresh がコンポーネントの状態を維持できるようにするための役立つヒントを提供するようになりました。Next.js が提供する各ヒントは完全に実用的であり、前後の例が付属しています。
  • コンポーネント状態がリセットされた場合の警告: ファイルが編集された後に Next.js がコンポーネントの状態を維持できない場合、詳細な警告が表示されるようになりました。この警告は、プロジェクトがコンポーネントの状態をリセットする必要があった理由を診断するのに役立ち、問題を修正して Fast Refresh を最大限に活用できるようにします。
  • 新しいドキュメント: Fast Refresh とは何か、その仕組み、および何を期待すべきかを説明する広範なドキュメントを追加しました。ドキュメントでは、エラー回復の仕組みを説明することにより、Fast Refresh をより適切に活用する方法も説明します。
  • ユーザーコードのトラブルシューティングガイド: 新しいドキュメントには、開発で Fast Refresh を最大限に活用する方法に関する一般的なトラブルシューティング手順とヒントも含まれています。

本番環境での React プロファイリング

React は少し前に、React コンポーネントのパフォーマンスの問題を追跡できるProfiler API を導入しました。この機能は開発では自動的に機能しますが、本番環境でプロファイリングするには、別のバージョンの ReactDOM を使用する必要があります。

Next.js 9.5 では、next build--profile フラグを使用することで、React の本番環境プロファイリングを有効にできるようになりました。

next build --profile

その後、開発の場合と同じ方法でプロファイラーを使用できます。

React のプロファイリングの詳細については、React チームによる React Profiler に関する投稿を参照してください。この機能の貢献者であるTODOrTotevおよび@darshkpatelに感謝します。

オプションのキャッチオールルート

Next.js 9.2 では、さまざまなユースケースでコミュニティに広く採用されているキャッチオール動的ルートのサポートを追加しました。キャッチオールルートを使用すると、ヘッドレス CMS、GraphQL API、ファイルシステムなどによって強化された高度な動的ルーティング構造を柔軟に作成できます。

フィードバックを聞いたところ、ユーザーはルートの最上位レベルに一致させるために、さらに柔軟性を求めていることがわかりました。本日、これらの高度なシナリオ向けに、オプションのキャッチオール動的ルートを発表できることを嬉しく思います。

オプションのキャッチオールルートを作成するには、[[...slug]] 構文を使用してページを作成できます。

たとえば、pages/blog/[[...slug]].js は、/blog と、/blog/a/blog/a/b/c などのその下のすべてのルートに一致します。

キャッチオールルートと同様に、slug はパス部分の配列としてルータークエリオブジェクトで提供されます。したがって、パス /blog/foo/bar の場合、クエリオブジェクトは { slug: ['foo', 'bar'] } になります。パス /blog の場合、クエリオブジェクトは slug キーを省略します: { }

ドキュメントで、オプションのキャッチオールルートの詳細を確認できます

Webpack 5 のサポート(ベータ版)

Webpack 5 は現在ベータ版です。これにはいくつかの大きな改善が含まれています。

本日、Next.js 用の webpack 5 のベータ版が利用可能になったことを発表できることを嬉しく思います。

webpack 5 を試すには、package.jsonYarn resolutionsを使用できます。

package.json
{
  "resolutions": {
    "webpack": "^5.0.0-beta.30"
  }
}

Webpack 5 ベータ版はすでに、nextjs.orgvercel.com の本番環境に展開されています。ぜひ段階的に試していただき、GitHub でフィードバックをお寄せください。

コンパイル基盤の改善

webpack 5 をサポートするために、コンパイルパイプラインの多くを Next.js に合わせて書き直しました。

  • Next.js は、webpack-hot-middlewarewebpack-dev-middleware に依存せず、webpack を直接使用し、Next.js プロジェクト向けに最適化するようになりました。これにより、アーキテクチャがシンプルになり、開発時のコンパイルが高速化されます。
  • オンデマンドエントリ(Next.js が開発中に特定のページにアクセスした際にコンパイルを行うシステム)も書き直され、私たちのユースケースに合わせて調整された新しい webpack の動作を利用することで、さらに信頼性が向上しました。
  • React Fast Refresh と Next.js Error Overlay は、webpack 5 と完全に互換性を持つようになりました。
  • ディスクキャッシュは、今後のベータ版リリースで有効になります。

後方互換性

私たちは常に、Next.js が以前のバージョンとの後方互換性を確保することに尽力しています。

Webpack 4 は引き続き完全にサポートされます。webpack 4 から 5 への移行が可能な限りスムーズになるよう、webpack チームと緊密に連携しています。

Next.js プロジェクトにカスタム webpack 設定がない場合、webpack 5 を最大限に活用するためにプロジェクトの変更は必要ありません。

重要: プロジェクトにカスタム webpack 設定がある場合は、webpack 5 に移行するためにいくつかの変更が必要になる場合があります。移行手順に注意するか、将来のアップグレードをシームレスにするために、webpack 拡張機能の使用を最小限に抑えることをお勧めします。

macOS でのファイル監視の改善

最近、macOS でコードを数回変更した後、ファイル監視が停止するという webpack の問題を発見しました。更新を確認するには、プロジェクトを手動で再起動する必要がありました。数回変更した後、サイクルが繰り返されました。

さらに、この問題は Next.js プロジェクトだけでなく、webpack 上に構築されたすべてのプロジェクトとフレームワークで発生することもわかりました。

数日間のデバッグの後、私たちはこの根本的な原因を webpack が使用する chokidar というファイル監視の実装に特定しました。これは Node.js エコシステムで広く使用されているファイル監視の実装です。

問題を修正するために、chokidar にパッチを送信しました。パッチがリリースされた後、Tobias Koppersと協力して、このパッチを新しい webpack バージョンに展開しました。

このパッチが適用された webpack バージョンは、Next.js 9.5 にアップグレードすると自動的に使用されます。

結論

Next.js の採用が引き続き拡大していることを嬉しく思っています。

  • 1,200 人を超える独立したコントリビューターがおり、9.4 リリース以降 135 人を超える新しいコントリビューターが加わりました。
  • GitHub では、プロジェクトは 51,100 回以上スターされています。

GitHub Discussions で Next.js コミュニティに参加しましょう。Discussions は、他の Next.js ユーザーとつながり、自由に質問したり、自分の作品を共有したりできるコミュニティスペースです。

たとえば、最初に プロジェクトの URL をみんなで共有する ことから始めてみましょう。

貢献したいが方法がわからない場合は、Webpack サポートのような実験的な機能を試して、フィードバックをお寄せください!

クレジット

このリリースを形作る上で助けとなった外部からのフィードバックや貢献を含め、コミュニティに感謝しています。

このリリースの複数の機能に貢献した長年の Next.js コミュニティメンバーであるJan Potomsに特別な感謝を申し上げます。

Next.js での webpack 5 サポートを実現する上で協力してくれた webpack の作者であるTobias Koppersに特別な感謝を申し上げます。

このリリースは、@chandan-reddy-k, @Timer, @aralroca, @artemisart, @sospedra, @prateekbh, @Prioe, @Janpot, @merceyz, @ijjk, @PavelK27, @marbiano, @MichelleLucero, @thorsten-stripe, @TODOrTotev, @Skn0tt, @lfades, @timneutkens, @akhila-ariyachandra, @chibicode, @rafaelalmeidatk, @kirill-konshin, @jamesvidler, @JeffersonBledsoe, @tylev, @jamesmosier, @filipemarins, @Remeic, @vvo, @timothyis, @jazibsawar, @coetry, @adam-zacharski, @danwilliams, @tywmick, @matamatanot, @goldins, @mvllow, @its-tayo, @sshyam-gupta, @wilbert-abreu, @sebastianbenz, @jaydenseric, @developit, @dylanjha, @darshkpatel, @spinks, @stefanprobst, @moh12594, @jasonmerino, @cristiand391, @HyunSangHan, @mcsdevv, @M1ck0, @hydRAnger, @alexej-d, @valmassoi, @motleydev, @eKhattak, @jpedroschmitz, @JerryGoyal, @bowen31337, @phillip055, @balazsorban44, @chuabingquan, @youhosi, @andresz1, @bell-steven, @areai51, @Wssn, @ndom91, @anthonyshort, @zxzl, @jbowes, @IamLizu, @PascalPixel, @ralphilius, @ysun62, @muslax, @elsigh, @AsherFoster, @botv, @tomdohnal, @christianalfoni, @tomasztunik, @gsimone, @illuminist, @jplew, @OskarKaminski, @RickyAbell, @steph-query, @ericgoe, @MalvinJay, @cristianbote, @Ashikpaul, @jensmeindertsma, @amorriscode, @abhik-b, @awareness481, @LukasPolak, @arvigeus, @romMidnight, @jackyef, @drumm2k, @kuldeepkeshwar, @bogy0, @Belco90, @wawjr3d, @tanmaylaud, @SarKurd, @kevinsproles, @dstotijn, @styfle, @blackwright, @BrunoBernardino, @heyAyushh, @Necmttn, @TrySound, @obedparla, @NyashaNziramasanga, @tonyspiro, @kukicado, @ceorourke, @MehediH, @robintom, @karlhorky, および @tcK1! の貢献によって実現しました。