Next.js 8
2019年2月11日(月)
投稿者:Connor Davis @ connordav_is、Shu Ding @ shuding_、Tim Neutkens @ timneutkens
本日、本番環境対応のNext.js 8をリリースできることを誇りに思います。以下の機能を搭載しています:
- Serverless Next.js
- ビルド時メモリ使用量の大幅削減
- ビルド時環境設定
- プリフェッチパフォーマンスの改善
- 初期HTMLサイズの縮小
- オンデマンドエントリの改善
- 開発環境でのポート待機の高速化
- Static Exportの高速化
- Head要素の重複排除
- 新しいcrossOrigin設定オプション
- インラインJavaScriptの削除
- API認証の例
いつものように、これらすべての利点が完全に後方互換性を保つよう努めています。ほとんどのNext.jsアプリケーションでは、以下を実行するだけです:
npm i next@latest react@latest react-dom@latest
コミュニティと私たちの成功に賭けてくださったすべての方々に感謝しています。前回のブログ投稿以来、AT&T、Starbucks、Twitchなどの企業がNext.jsで公開Webサイトやアプリを再ローンチしているのを見てきました。
Serverless Next.js
Next.jsのserverlessターゲットは、ページからServerless関数を出力します。
Serverlessデプロイメントは、アプリケーションをより小さな部分(lambdaとも呼ばれる)に分割することで、信頼性とスケーラビリティを劇的に向上させます。Next.jsの場合、pagesディレクトリ内の各ページがserverless lambdaになります。
serverlessには多くの利点があります。参照されたリンクではExpressのコンテキストでいくつかについて説明していますが、原則は普遍的に適用されます:serverlessは分散された障害点、無限のスケーラビリティを可能にし、「使った分だけ支払う」モデルで非常に手頃な価格です。
Next.jsでserverlessモードを有効にするには、next.config.jsにserverlessビルドターゲットを追加します:
module.exports = {
target: 'serverless',
};
serverlessターゲットは、ページごとに単一のlambdaを出力します。このファイルは完全にスタンドアロンで、実行に依存関係を必要としません:
pages/index.js => .next/serverless/pages/index.js
pages/about.js => .next/serverless/pages/about.js
Next.js Serverless関数のシグネチャは、Node.js HTTPサーバーコールバックに似ています:
type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;
voidは関数が戻り値を持たないことを指し、JavaScriptのundefinedと同等です。関数を呼び出すとリクエストが完了します。
Next.jsは、ホスティングプラットフォームが異なる関数シグネチャを持つため、serverlessデプロイメント用の低レベルAPIを提供します。一般的には、Next.js serverlessビルドの出力を互換性レイヤーでラップしたいでしょう。
例えば、プラットフォームがNode.jsのhttp.Serverクラスをサポートしている場合:
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'));
まとめ
- serverlessデプロイメントを実装するための低レベルAPI
- pagesディレクトリ内のすべてのページがserverless関数(lambda)になる
- 可能な限り小さなserverless関数を作成(50KBベースzipサイズ)
- 関数のコールドスタートの高速化に最適化
- serverless関数は0個の依存関係を持つ(関数バンドルに含まれる)
- Node.jsの
http.IncomingMessageとhttp.ServerResponseを使用
- next.config.jsで
target: 'serverless'を使用してオプトイン
- serverターゲットは引き続き完全にサポートされ、メンテナンスされる
publicRuntimeConfigとserverRuntimeConfigはserverlessモードではサポートされない。代わりにビルド時設定を使用
ビルド時メモリ使用量の大幅削減
Next.js(およびwebpackエコシステムの残りの部分!)のビルドパフォーマンスとリソース使用率を改善するため、webpackに貢献しました。この取り組みにより、パフォーマンスの低下なしに最大16倍のメモリ使用量改善を実現しました。
メモリはより迅速に解放され、多くのストレス(多数のページ)下でプロセスがクラッシュしなくなりました。この最適化をどのように達成したかについて、近日中に詳しく説明します。Next.jsブログにご注目ください。
ビルド時環境設定
Next.jsアプリケーションをレビューする際によく見られるパターンは、アプリケーションに設定値を提供するためにbabel-plugin-transform-defineやwebpack.DefinePluginを追加することでした。
Next.js 8では、後方互換性のある方法で同じ機能を提供するため、next.config.jsにenvという新しいキーを導入しています:
module.exports = {
env: {
customKey: 'MyValue',
},
};
これにより、コード内でprocess.env.customKeyを使用できます。例えば:
export default function IndexPage() {
return <h1>The value of customKey is: {process.env.customKey}</h1>;
}
process.env.customKeyはビルド時に'MyValue'に置き換えられます。
プリフェッチパフォーマンスの改善
Next.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では、プリフェッチは<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.0 | 1.50KB | - |
| 8.0 | 1.16KB | 23%小さく |
サイズを削減した主な方法は:
- ページ初期化インラインスクリプトを削除
/_errorページがすべてのページ読み込みに含まれなくなった
/_errorのオンデマンド読み込み
本番環境でエラーが発生すると、エラーが発生したことを表示するために/_errorページがレンダリングされます。Next.jsの最初のリリース以来、/_errorページのscriptタグは初期HTMLの一部であり、ランタイムエラーがなければ使用されないにもかかわらず読み込まれていました。
Next.js 8以降、/_errorページはエラーが発生したときにオンデマンドで読み込まれます。つまり、デフォルトで読み込み、解析、実行されるコードが少なくなります。
DXの改善
Next.jsの主要な目標の一つは、可能な限り最高の開発者体験で最高の本番パフォーマンスを提供することです。このリリースには、ユーザーフィードバックに基づく多くの微細な改善が含まれています。
オンデマンドエントリの改善
Next.jsは、開箱即用で、アクティブに開発されているページのみを自動的にコンパイルします。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がトリガーされるたびに、ブラウザ開発ツールのコンソールとネットワークタブに表示されていました。
最も要求された機能の一つは、これらのリクエストが不要なノイズを追加する可能性があるため、ブラウザ開発者ツールからこれらのリクエストを隠す機能でした。
Next.js 8では、fetchベースのpingがWebSocketsベースのアプローチに置き換えられたことを発表できることを嬉しく思います。つまり、pingは引き続き発生しますが、WebSocket接続を検査するときにのみ表示されます。
WebSocketsへの変換に協力してくれたJJ Kasperに特別な感謝を。
開発環境でのポート待機の高速化
Next.js開発サーバーを起動する際、リクエストを処理できるようにするためにいくつかの初期コンパイルを実行する必要があります。デフォルトでは、Next.jsはHTTPサーバーを開始する前にこのコンパイル手順の完了を待っていました。つまり、next devを実行してからブラウザに移動すると、HTTPサーバーがまだ接続を待機していないため、「このサイトにアクセスできません」というメッセージが表示されることがありました。
Next.js 8では、コンパイルが開始される前にHTTPが接続を待機するようになりました。つまり、コンパイルが完了する前にhttp://localhost:3000/にアクセスすると、利用可能になるまでページを更新する必要がなく、リクエストは初期コンパイルの完了を待ってからリクエストを処理します。
この機能を実装してくれたBrian Beckに特別な感謝を。
Static Exportの高速化
Next.jsは、高パフォーマンスを実現する手段として事前レンダリングのアイデアに焦点を当てています。事前レンダリングには2つの形式があります:
- サーバーレンダリング:各リクエストがレンダリングをトリガーします。その結果、エンドユーザーはデータの消費を開始するためにJSのダウンロードを待つ必要がありません
- 静的レンダリング:サーバー上でのコード実行なしに直接提供できる静的ファイルを出力します
Next.js 8以降、マシンに複数のCPUがある場合、next exportによる静的レンダリングは速度改善があります。4つのCPUコアを持つMacBookでのテストに基づくと、すべてのコアを活用してページを事前レンダリングすることで、エクスポート速度は1秒あたり約25ページから75ページに向上しました。
Next.jsは自動的にCPUコア数を検出し、それに応じてページを分散します。コード変更は必要ありません。
この機能を実装してくれたBenjamin Knifflerに特別な感謝を。
Head要素の重複排除
アプリケーションを構築する際の一般的なニーズは、ページの<head>要素を更新することです。例えば、レスポンシブデザインのために<title>や<meta name="viewport">を設定することです。
Next.jsは、<head>に変更を加えるための組み込みコンポーネントを公開しています:
import Head from 'next/head';
export default function IndexPage() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
);
}
<Head>コンポーネントは、異なるコンポーネントで複数回使用することもできます。例えば、レイアウトコンポーネントがデフォルトのheadタグを設定できます。
しかし、デフォルトのheadタグを異なる値でオーバーライドしたい場合があります。Next.jsの古いバージョンでは、タグを重複排除する方法がなかったため、出力でタグが重複していました。
このため、<Head>コンポーネント内のすべての要素にkeyプロパティを提供することが可能になり、同じキー値を持つタグを自動的に重複排除します。2つのタグにkey="viewport"を設定すると、最後のもののみがレンダリングされます。