Next.js 9.0.7
公開日: 2019-09-30
Next.js 9.0 は約2か月前にリリースされて以来、9.0.1、9.0.2、9.0.3、9.0.4、9.0.5、9.0.6、そして 9.0.7 と、破壊的変更なしでいくつかの重要な小規模リリースを行ってきました。本稿ではこれらのリリースで追加・改善された点を解説します。
主な改善点:
- Windows 環境でのビルド並列処理の改善
- デフォルトでの Gzip 圧縮の有効化
- TypeScript のエラー報告をアクティブなページのみに限定
- 匿名テレメトリによる利用状況の把握(オプトアウト可能)
- next build のコンソールに進行インジケータ(ドット)を追加
- next/head の要素トラッキング方式の改善(余分な class を削除)
- pages ディレクトリ内の“非ページ”の誤配置を防止する検証
- ランタイムの不要部分をアプリで使われているときのみ含める最適化
Windows 環境での並列処理の改善
next build はビルド出力を Terser で並列に minify するなど、複数箇所で並列処理を行います。以前は worker-farm を使って多CPUにまたがる並列化を行っていましたが、Windows 上で一貫して動作しないケースがあることが判明しました(多くの Windows ユーザーはカスタム webpack により minify を無効化していることも分かりました)。
この問題を解決するために worker-farm から jest-worker に移行しました。jest-worker は Jest テストランナーで使われる並列化用パッケージで、信頼性が高くメンテナンスされています。さらに jest-worker は Node 12 で導入された worker_threads をサポートしており、child_process と異なりメモリを共有できるため、新しい Node バージョン上で高速なビルドが可能になります。これにより macOS、Linux、Windows でビルドの並列処理が再有効化され、より安定しました。
デフォルトでの Gzip 圧縮
多くの企業がカスタムサーバーを使う理由の一つがレスポンスの圧縮であることが分かりました。Express ミドルウェアの compression は HTTP レスポンスの Gzip 圧縮を行います。本来は Nginx のようなリバースプロキシで行うべき処理ですが、調査では多くのユーザーが圧縮を設定していませんでした。
Vercel のようなプラットフォームではプロキシレベルで gzip や brotli が自動的に処理されますが、セルフホスティング時は自分で compression(またはリバースプロキシ)を追加する必要があります。Next.js 9.0.4 以降、next start またはカスタム server.js を使う場合にデフォルトで gzip 圧縮が有効になりました。Node が Brotli をネイティブにサポートしたため、brotli サポートも近日追加予定です。
既にカスタムサーバーで圧縮を有効にしている場合、Next.js は追加の圧縮器を挿入しません。serverless ターゲットではアセットが別途アップロードされ Node.js を経由して配信されないため、デフォルトで compression は含めていません。Vercel 等の圧縮を自動で処理するプラットフォームへデプロイしている場合は何も変更する必要はありません。
TypeScript: アクティブなページのみレポート
Next.js 9 から TypeScript を組み込みサポートしています。単にページの拡張子を .js から .tsx に変えるだけで Next.js が残りのセットアップを自動で処理または案内します。TypeScript の型チェックは開発中に tsc --watch を並列で実行して行われます。
開発時、Next.js には on-demand entries(オンデマンドエントリ)という概念があり、現在作業中のページのみをコンパイルします。9.0.4 以降、TypeScript のエラー報告は on-demand entries によってアクティブにコンパイルされているページのみに限定されるようになり、不要な型チェックのノイズが減少します。フルアプリケーションの型チェックは next build 実行時に行われるか、エディタ内で実行できます。
テレメトリ(匿名データ収集)
Next.js はリリース以来多くの成長を遂げましたが、改善の優先度決定にはコミュニティからのフィードバックに頼ってきました。しかしそのアプローチだと一部のユーザー群からしか情報が得られず、全体像がつかみにくいという問題があります。そこで、匿名かつ自動化されたテレメトリを導入し、Next.js の利用状況をより広範に把握できるようにしました。これによりどの機能や使い方を優先的に最適化すべきか、改善が効果を発揮しているかを検証できます。
テレメトリの詳細とオプトアウト方法は https://nextjs.org/telemetry を参照してください。
ビルド進行を示すドット(next build)
ユーザーのフィードバックで、next build 実行中に何も起きていないように見える(ビジュアル的に停止しているように見える)ことが指摘されました。これを解決するため、next build のコンソール出力に読み込みインジケータ(ドット)を追加しました。この出力はコマンドが実行中でプロセスがフリーズしていないことを視覚的に示します。将来的にはビルドの各段階をより詳細に表示する予定です。
next/head の要素トラッキング改善
Next.js は HTML の <head> を管理するために next/head モジュールを提供しています。例えばページにタイトルを追加するコード例は次の通りです。
import Head from 'next/head';
export default function MyPage() {
return (
<>
<Head>
<title>My Title</title>
</Head>
<h1>Hello World</h1>
</>
);
}
SSR(サーバーサイドレンダリング)による HTML をレンダリングする際、Next.js は <Head> 内でレンダリングされたコンポーネントを収集し、ページの <head> にタグを出力します。さらに <Link> コンポーネントによるクライアントサイドの SPA 的なページ遷移時は、前のページで注入された <head> 要素をクリーンアップする必要があります。
以前は next/head によって注入された要素すべてに class="next-head" を付与してトラッキングしていました(例: <title class="next-head">My Title</title>)。しかし一部ユーザーから外部サービスのスクリプトがこの余分な class によって検証エラーになるとの報告がありました。
Google Chrome チームの Gerald Monaco による提案により、要素にクラスを付ける代わりに、next/head がレンダリングした要素数を保持する追加の <meta> タグを挿入し、そのカウントを用いて既存要素をクリーンアップする方式に変更しました。これにより、複数の要素が <head> に注入される場合の初期 HTML ペイロードを削減できます。
pages ディレクトリ内の非ページ検出
Next.js の convention は pages ディレクトリ内のファイルがアプリケーションのルートになる、というものです(例: pages/about.js → /about)。しかし一部ユーザーがコンポーネント構造をすべて pages ディレクトリ内に置いてしまい、結果的に全コンポーネントがページとして扱われ、ビルド性能が著しく低下するケースがありました。無効な“ページ”に対して 2 つ以上の JS ファイルが生成されるなどのオーバーヘッドが発生し、コード分割のヒューリスティクスにも影響しました。
これを防ぐため、Next.js 9 では pages ディレクトリ内のファイルが React コンポーネントを export しているかどうかを検証し、潜在的な非ページが見つかった場合はエラーメッセージで移動を促します。これにより開発/本番ビルドやコード分割がより高速かつ正確になります。
ランタイムの改善
Next.js フレームワークは多くのパーツから成り、クライアントサイドランタイムはハイドレーションやクライアントルーティングなどを担います。ハイドレーションはサーバー側でレンダリングされた HTML をインタラクティブにするために必要で、イベントハンドラの追加や useEffect()/componentDidMount の呼び出しなどを行います。
Next.js は単純なハイドレーション以上のことを行います。クライアントサイドルータのセットアップ、next/head の設定、next/dynamic を通じた動的ロードなど、それぞれに固有のランタイム部分があります。例えば next/dynamic を使うとサーバーでレンダリングされた遅延ロードコンポーネントをクライアント側で準備する必要があり、そのための追加バンドルが生成されます。
以前はこうしたランタイムは常に Next.js のランタイムバンドルに含まれていましたが、今回の改善で next/dynamic を使用している場合にのみ該当ランタイムを含めるようになりました。結果として next/dynamic を使っていないアプリケーションではダウンロード/解析/実行する JavaScript が減ります。
AppTree サポート
一部の React エコシステムのライブラリはサーバーサイド...