Backブログに戻る

Next.js 8

Next.js 8ではサーバーレスモード、バンドルサイズの縮小、パフォーマンス改善などが導入されました。

本日、Next.js 8の正式リリースを発表できることを誇りに思います。主な新機能は以下の通りです:

これまでと同様、これらの改善はすべて完全な後方互換性を保っています。ほとんどのNext.jsアプリケーションでは、次のコマンドを実行するだけでアップグレードできます:

Terminal
npm i next@latest react@latest react-dom@latest

私たちのコミュニティと成功を信じてくれたすべての方々に感謝します。前回のブログ投稿以降、AT&TStarbucksTwitchなどの企業がNext.jsを使用して公開向けウェブサイトやアプリをリニューアルしました。

サーバーレスNext.js

Next.jsのサーバーレスターゲットは、ページからサーバーレス関数を出力します

サーバーレスデプロイはアプリケーションを小さな部分(ラムダとも呼ばれます)に分割することで、信頼性とスケーラビリティを大幅に向上させます。Next.jsの場合、pagesディレクトリ内の各ページがサーバーレスラムダになります。

サーバーレスには多くの利点があります。参照リンクではExpressの文脈で説明されていますが、その原則は普遍的です: サーバーレスは障害ポイントの分散、無限のスケーラビリティを可能にし、「使用した分だけ支払う」モデルで非常にコスト効率が良いです。

Next.jsでサーバーレスモードを有効にするには、next.config.jsserverlessビルドtargetを追加します:

next.config.js
module.exports = {
  target: 'serverless',
};

serverlessターゲットはページごとに単一のラムダを出力します。このファイルは完全にスタンドアロンで、実行に依存関係を必要としません:

  • pages/index.js => .next/serverless/pages/index.js
  • pages/about.js => .next/serverless/pages/about.js

Next.jsサーバーレス関数のシグネチャはNode.js HTTPサーバーコールバックと似ています:

type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;

Next.jsはサーバーレスデプロイのための低レベルAPIを提供します。ホスティングプラットフォームごとに異なる関数シグネチャがあるためです。一般的には、Next.jsサーバーレスビルドの出力を互換性レイヤーでラップする必要があります。

例えば、プラットフォームがNode.js http.Serverクラスをサポートしている場合:

server.js
const http = require('http');
const page = require('./.next/serverless/about.js');
const server = new http.Server((req, res) => page.render(req, res));
server.listen(3000, () => console.log('Listening on http://localhost:3000'));

まとめ

  • サーバーレスデプロイ実装のための低レベルAPI
  • pagesディレクトリ内の各ページがサーバーレス関数(ラムダ)になります
  • 可能な限り最小のサーバーレス関数を作成(50 KBベースのzipサイズ)
  • 関数のコールドスタート高速化に最適化
  • サーバーレス関数には依存関係がありません(関数バンドルに含まれています)
  • Node.jsのhttp.IncomingMessagehttp.ServerResponseを使用
  • next.config.jstarget: 'serverless'を設定してオプトイン
  • serverターゲットも完全にサポートされ、メンテナンスされます
  • serverlessモードではpublicRuntimeConfigserverRuntimeConfigはサポートされていません。代わりにビルド時設定を使用してください。

ビルド時のメモリ使用量大幅削減

私たちはwebpackに貢献し、Next.js(およびwebpackエコシステム全体)のビルドパフォーマンスとリソース利用を改善しました。

この取り組みにより、最大16倍のメモリ使用効率向上を達成し、パフォーマンスの低下はありませんでした。

メモリがより速く解放され、多くのページがある場合でもプロセスがクラッシュしなくなりました。

この最適化の詳細については、近日中にNext.jsブログで詳しく説明する予定です。

ビルド時環境設定

Next.jsアプリケーションをレビューする中で、babel-plugin-transform-definewebpack.DefinePluginを追加してアプリケーションに設定値を提供するというパターンが頻繁に見られました。

Next.js 8では、next.config.jsenvという新しいキーを導入し、同じ機能を後方互換性のある方法で提供します:

next.config.js
module.exports = {
  env: {
    customKey: 'MyValue',
  },
};

これにより、コード内でprocess.env.customKeyを使用できるようになります。例:

pages/index.js
export default function IndexPage() {
  return <h1>The value of customKey is: {process.env.customKey}</h1>;
}

process.env.customKeyはビルド時に'MyValue'に置き換えられます。

プリフェッチ性能の改善

Next.jsルーターでは、ページをプリフェッチしてナビゲーションを高速化できます:

pages/index.js
import Link from 'next/link';
 
export default function IndexPage() {
  return (
    <>
      <Link href="/about" prefetch>
        <a>To About Page</a>
      </Link>
    </>
  );
}

これは、prefetch属性を持つすべてのリンクのJavaScriptバンドルをプリフェッチすることで機能します。

Next.js 8以前のバージョンでは、これはドキュメント<body><script>タグを挿入することを意味していました。

しかし、これによりページを開く際にいくつかのオーバーヘッドが生じ、特にブラウザの「読み込み中」表示が、ページが既に操作可能になっているにもかかわらず、予想以上に長く表示されることがありました。

Next.js 8では、prefetch<script>タグの代わりに<link rel="preload">を使用します。また、onloadの後にのみプリフェッチを開始し、ブラウザがリソースを管理できるようにします。

さらに、Next.jsは2Gインターネット接続とnavigator.connection.saveDataモードを検出し、遅いネットワーク接続ではプリフェッチを無効にします。

初期HTMLサイズの縮小

Next.jsはHTMLをプリレンダリングするため、ページを<html><head><body>と、ページをレンダリングするために必要なJavaScriptファイルで囲むデフォルト構造にラップします。

Next.js 7では初期ペイロードを1.50KBに最適化し、前バージョンから7.4%削減しました。

さらに初期ペイロードサイズを1.16KBに削減し、23%のさらなる削減を達成しました:

7.08.0差分
ドキュメントサイズ(サーバーレンダリング)1.50KB1.16KB23% 縮小

主なサイズ削減方法:

  • ページ初期化インラインスクリプトが削除されました
  • /_errorページが毎回のページロードに含まれなくなりました

/_errorのオンデマンドロード

本番環境でエラーが発生すると、/_errorページがレンダリングされ、エラーが発生したことが表示されます。

Next.jsの最初のリリース以来、/_errorページのスクリプトタグは初期HTMLの一部であり、ランタイムエラーが発生しなくてもロードされていました。

Next.js 8から、/_errorページはエラー発生時にオンデマンドでロードされます。

つまり、デフォルトでロード、解析、実行されるコードが少なくなります。

開発者体験の改善

Next.jsの主要な目標の1つは、最高の開発者体験で最高の本番パフォーマンスを提供することです。このリリースには、ユーザーフィードバックに基づいた多くの細かな改善が含まれています。

オンデマンドエントリの改善

Next.jsはデフォルトで、開発中に_アクティブに_使用されているページのみを自動的にコンパイルします。next devを実行するたびにpagesディレクトリ内のすべてのページをコンパイルするのではなく、アクセスしたページをオンデマンドでコンパイルします。

例えば、http://localhost:3000/my-pageにアクセスすると、pages/my-page.jsファイルがオンデマンドでコンパイルされた後、ページがレンダリングされます。

これにより、開発者は開発サーバーを起動する際にすべてのページのコンパイルを待つ必要がなく、大規模なアプリではかなりの時間がかかる場合があります。コンパイラはバンドリング時にすべてのページを考慮する必要がないため、メモリ使用量を低く保ち、コンパイラを高速に保ちます。

オンデマンドエントリのフロー

オンデマンドエントリのフロー

ページが25秒間アクセスされないと、コンパイラのビルドキャッシュから破棄され、コンパイラを高速に保ち、メモリ使用量を削減します。

Next.jsがページのアクセスを追跡する方法は、ポーリングメカニズムを使用しています。5秒ごとに「on-demand-entries-ping」が送信され、Next.js開発サーバーに特定のページがアクセスされていることを通知します。

この機能の初期リリース以来、pingはwindow.fetch呼び出しを使用して行われていました。つまり、pingがトリガーされるたびに、ブラウザの開発ツールのconsolenetworkタブに表示されます。

最も要望の多かった機能の1つは、これらのリクエストをブラウザの開発ツールから隠す機能です。これらのリクエストは不要なノイズを追加する可能性があります。

Next.js 8では、fetchベースのpingがWebSocketベースのアプローチに置き換えられました。つまり、pingは依然として行われますが、WebSocket接続を検査する場合にのみ表示されます。

WebSocketへの変換に協力してくれたJJ Kasperに特に感謝します。

開発時のポート待機高速化

Next.js開発サーバーを起動する際、リクエストを処理できるようにするために初期コンパイルを実行する必要があります。デフォルトでは、Next.jsはこのコンパイルステップが完了するまでHTTPサーバーを開始しませんでした。つまり、next devを実行してからブラウザに移動すると、HTTPサーバーがまだ接続を待機していないため、「このサイトにアクセスできません」というメッセージが表示されることがありました。

Next.js 8では、HTTPサーバーはコンパイルが開始される前に接続を待機するようになります。つまり、コンパイルが完了する前にhttp://localhost:3000/にアクセスしても、リクエストは初期コンパイルが完了するのを待ってから処理され、利用可能になるまでページを更新する必要がなくなります。

この機能を実装してくれたBrian Beckに特に感謝します。

静的エクスポートの高速化

Next.jsは、高性能を実現する手段としてプリレンダリングのアイデアに焦点を当てています。プリレンダリングには2つの形式があります:

  • サーバーサイドレンダリング (SSR) - 各リクエストがレンダリングをトリガーします。その結果、エンドユーザーはデータの利用を開始するためにJSのダウンロードを待つ必要がありません
  • 静的レンダリング - サーバー上でコードを実行する必要なく直接提供できる静的ファイルを出力します

Next.js 8から、next exportによる静的レンダリングは、マシンに複数のCPUがある場合に速度が向上します。

4コアCPUのMacBookでのテストでは、すべてのコアを利用してページをプリレンダリングすることで、エクスポート速度が約1秒あたり25ページから75ページに向上しました。

Next.jsは自動的にCPUコア数を検出し、それに応じてページを分散します。コード変更は必要ありません。

この機能を実装してくれたBenjamin Knifflerに特に感謝します。

Head要素の重複排除

アプリケーションを構築する際の一般的なニーズは、ページの<head>要素を更新することです。例えば、<title>を設定したり、レスポンシブデザインのために<meta name="viewport">を設定したりします。

Next.jsは<head>に変更を加えるための組み込みコンポーネントを公開しています:

pages/index.js
import Head from 'next/head';
 
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  );
}

<Head>コンポーネントは、異なるコンポーネントで複数回使用することもできます。例えば、レイアウトコンポーネントでいくつかのデフォルトのheadタグを設定できます。

ただし、デフォルトのheadタグを異なる値で上書きしたい場合があります。Next.jsの以前のバージョンでは、タグの重複排除方法がなかったため、出力でタグが重複していました。

このため、<Head>コンポーネント内のすべての要素にkeyプロパティを提供できるようになり、同じkey値を持つタグを自動的に重複排除します。

2つのタグにkey="viewport"を設定すると、最後のタグのみがレンダリングされます。

pages/index.js
import Head from 'next/head';
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>My page title</title>
        <meta
          name="viewport"
          content="initial-scale=1.0, width=device-width"
          key="viewport"
        />
      </Head>
      <Head>
        <meta
          name="viewport"
          content="initial-scale=1.2, width=device-width"
          key="viewport"
        />
      </Head>
    </>
  );
}

セキュリティの改善

新しいcrossOrigin設定オプション

Next.js 6では、pages/_document.js<Head><NextScript>crossOrigin属性を追加するオプションを導入しましたが、これはcross-originを設定するためのすべてのユースケースをカバーしていませんでした。

Next.jsには動的に<script>タグを注入するクライアントサイドルーターがあり、これらのタグには注入時にcross-origin属性が欠落していました。

すべての<script>タグにcross-originが設定されていることを保証するために、next.config.jsに新しい設定オプションを導入しました

next.config.js
module.exports = {
  crossOrigin: 'anonymous',
};

このオプションを導入するもう1つの利点は、アプリケーションでcross-originを設定するためにカスタムpages/_document.jsが不要になったことです。

以前の動作も引き続きサポートされていますが、開発者が新しく導入されたオプションに移行するのを助けるために、開発時に警告が表示されます。

インラインJavaScriptの削除

Next.js 7以前を使用する場合、コンテンツセキュリティポリシー (CSP)を有効にするためには、ユーザーはポリシーにscript-src 'unsafe-inline'を含める必要がありました。これは、Next.jsがデータをクライアントサイドに渡すために(例えばgetInitialPropsの結果を渡すために)インラインの<script>タグを生成していたためです。

Next.js 8では、このインラインスクリプトタグを安全にクライアントに転送するためのJSONタグに変更しました。これにより、Next.jsが含めるインラインスクリプトはなくなりました。

慎重に検討した上で、script-src 'self'を使用できるようになりました。

API認証の例

これまで最もリクエストの多かった例の1つは、Next.jsで外部API(どのようなAPIでも、どのプログラミング言語でも)に対して認証を行う方法でした。

Next.js 8のリリースに伴い、新しく作成したサンプルも紹介します: with-cookie-auth

この例では、外部のNode.js APIに対して認証を行う方法を示していますが、適用されているコンセプトはどのようなステートレスAPIでも機能します。

この例では、サーバーサイドとクライアントサイドのレンダリング間でトークンを共有するためにクッキーを使用しています。

この方法により、アプリがサーバーでレンダリングされた場合でも、ユーザーに代わって認証済みデータを取得できます。

このサンプルを提供してくれたJuan Olveraに特別な感謝を捧げます。

コミュニティ

最初のリリース以来、Next.jsはフォーチュン500企業から個人ブログまであらゆる場面で使用されてきました。Next.jsの採用が継続的に成長していることを非常に嬉しく思っています。

  • 600人以上のコントリビューターが少なくとも1つのコミットをしています。
  • GitHubでは、このプロジェクトは34,400回以上スターされています。
  • 最初のリリース以来、2600以上のプルリクエストが提出されました。

Next.jsコミュニティには4,570人以上のメンバーがいます。ぜひ参加してください!