本文へスキップ
ブログに戻る

2020年7月27日 月曜日

Next.js 9.5

投稿者

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

安定したIncremental Static Regeneration

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

両方の利点を最大限に引き出すために、Next.jsはサイトを既にビルドした後で静的コンテンツを更新する**Incremental Static Generation**を導入しました。getStaticPathsfallback: trueオプションを使用することで、**ランタイム**で**新しい静的ページ**を**登録**できます。

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

本日、**Incremental Static Re-generation**の_一般提供_を発表します。これは、トラフィックが流入する際に既存のページをバックグラウンドで再レンダリングすることで、**既存のページを更新する**メカニズムです。

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フラグは、キャッシュスタンピードを防ぐために、最大で1回の生成が行われる秒数を表します。

従来のSSRとは異なり、Incremental Static Regenerationは静的サイトの利点を確実に維持します。

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

インクリメンタル機能(ページの追加と遅延更新)とプレビューモードの両方が安定し、next startVercelエッジプラットフォームの両方で箱から出してすぐに完全にサポートされています。

この新機能を披露するために、特定の問題に対するさまざまな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ウェブフックなど)をリッスンして再生成する

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

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

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を使用すると、ウェブブラウザに以下の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機能の詳細については、rewritesのドキュメントをご確認ください。

リダイレクト

ほとんどのウェブサイトでは、少なくともいくつかのリダイレクトが必要です。特にプロジェクトのルート構造を変更する場合。例えば、/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機能の詳細については、redirectsのドキュメントをご確認ください。

ヘッダー

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

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

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機能の詳細については、headersのドキュメントをご確認ください。

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

3年前にNext.jsが導入された際、そのデフォルトの動作は、末尾にスラッシュのあるすべての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に感謝します。

オプショナルなCatch All ルート

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エラーオーバーレイは現在webpack 5と完全に互換性があります。
  • ディスクキャッシュは将来のベータリリースで有効になります。

後方互換性

Next.jsが以前のバージョンとの後方互換性を持つことを常に保証するよう努めています。

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

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

重要: プロジェクトにカスタムwebpack設定がある場合、webpack 5への移行には一部変更が必要になる場合があります。今後のスムーズなアップグレードのため、移行手順に注意を払うか、webpack拡張機能の使用をできるだけ最小限に抑えることをお勧めします。

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

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

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

数日間のデバッグの結果、この問題の根本原因は、webpackが使用しているファイル監視実装であるchokidarに起因していることが判明しました。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, and @tcK1!