commmune Developer Blog

commmune開発者ブログ

【全文和訳】Next.js 9.0.7

(以下は2019年9月30日に公開された Next.js 9.0.7 の日本語和訳です。 以下原文 )

https://nextjs.org/blog/next-9-0-7

約2か月前、Next.js 9.0がリリースされました。以来、私たちは9.0.1、9.0.2、9.0.3、9.0.4、9.0.5、9.0.6、および9.0.7といった、小さいものの、非常に重要な7つのリリースの開発に取り組みました。

重大な変更点はありませんが、これらのリリースによって皆さんのウェブサイトやアプリに係る点をご説明いたします。
(訳者注* アップデート機能名は原文のまま)

  • Improved Concurrency in Windows Environments:
    Windowsでの’next build’プロセスの信頼性が向上し、作業の並行化が向上しました。

  • Gzip Compression by Default:
    最適化のステップを省略するため、Gzip圧縮がデフォルトで追加されました。

  • TypeScript Report on Active Pages Only:
    ビルトインされているTypeScriptをアクティブページの変更のみを感知するように拡張したことにより、速度と信頼性の向上を図りました。

  • Telemetry:
    私たちが今後Next.jsのどの部分を最適化し、さらに最適化された箇所が意図した効果を発揮しているか検証することが可能になりました。

  • Improved next/head Element Tracking:
    ‘next-head’クラスが削除されたことにより、実装を検証するツールの実装が容易になりました。

  • Preventing Non-Pages in the Pages Directory:
    Non-pagesが意図せず公開されることを未然に防ぐことにより、アプリケーションのセキュリティと‘next-build’にかかる時間を最適化します。

  • Runtime Improvements:
    例えば、next / dynamicをはじめとしたNext.jsの特定の部分が使用されていない場合、Runtime時にこれらが不要になり、バンドルサイズが小さくなります。

  • AppTree Support:

  • Removal of the next-server Package:

Improved Concurrency in Windows Environments

Next.jsは、next buildプロセス中に多くの並行処理を行います。 主な用途は、Terserと並行して、ビルド出力を最小化することです。

以前、この作業はworker-farmというパッケージを使用して、複数のCPUで処理されていました。 しかし、多くのWindowsユーザーがカスタムwebpack構成で最小化機能を無効にしていることを私たちは気づきました。さらに調査したところ、worker-farmは、Windowsマシンでは一貫した動作が認められませんでした。

この問題を解決するために、私たちはworker-farmからjest-workerに移行しました。 これにより、MacOSLinuxWindowsの各マシンでビルドの信頼性と一貫性が確保されました。

jest-workerは、その名前が示すように、Jest test runnerの一部です。これは、Jestがテストケースの並列化に使用するパッケージです。 このパッケージには実績があり、信頼性が高く、メンテナンスも行き届いています。

jest-workerは、Node 12の新機能である‘worker_threads’もサポートしています。child_procesとは異なり、worker_threadsはメモリを共有できます。つまり、新しいバージョンのNodeでのビルド時間が短縮されます。

jest-workerに移行したことにより、Windowsユーザーのビルドの並行処理を再び有効にすることができました。


Gzip Compression by Default

企業がカスタムサーバーを使用する理由を調査した結果、最も頻繁な理由は圧縮処理のためだということがわかりました。 多くの企業は、HTTPレスポンスをGzipに圧縮処理するExpress Middlewareを追加して、対応していました。

このMiddlewareはレスポンスを圧縮することにより、ユーザーに送られるバイトの数を少なくすることができます。通常、このような処理は、NginxのようなReverse Proxyによって行われる必要があります。Reverse Proxyは、Single-Threaded Node ProcessにおけるCPU負荷の高い処理をなくすことができます。

しかしながら、WebにおけるNext.jsの使用状況を調べると、多くの企業が圧縮設定を行っていないことが分かりました。

ZEIT Nowなどのプラットフォームでは、gzipbrotliがプロキシレベルで自動的に処理されます。

セルフホスティングの場合、企業は自分でgzipを(compressionまたはリバースプロキシを使用して)追加する必要があります。

Next.js 9.0.4以降では、next startまたはカスタムserver.jsを使用する場合、gzip圧縮がデフォルトで含まれています。

Node.jsがネイティブでBrotliをサポートするようになったため、brotliのサポートもまもなく開始されます。

既にカスタムサーバー経由で圧縮処理が皆さんのアプリケーションに設定されている場合、Next.jsは独自の圧縮処理を追加しません。

Serverless Targetに対し、Next.jsはデフォルトでは圧縮処理を行いません。理由としては、Serverless Targetを使用する場合、AssetはNode.js経由ではなく、単体でアップロードされるからです。

ZEIT Nowをはじめとした、圧縮処理を行うプラットフォームを既に導入されている場合、特段変更は必要ありません。


TypeScript Report on Active Pages Only

Next.js 9には、TypeScriptのビルトインサポートが含まれています。ページの名前を.jsから.tsxに変更するだけでこの機能は使用可能です。 他にセットアップが必要な場合、Next.jsはこれらを自動的行うか、皆さんに必要な手順をガイドしてくれます。

開発プロセスと並行して、Next.jsは’tscwatch’を実行することで、型チェックを行います。Next.jsにはon-demand entriesと呼ばれる概念があります。 この機能は、作業中のページのみをコンパイルします。詳細についてはこちらをご覧ください。

f:id:commmune:20191016213241j:plain
The on-demand entries flow

9.0.4では、On-demand entriesによってコンパイルされているType errorのみ報告されます。これにより、特定のページのみに作業を集中している際の型チェックノイズを大幅に軽減することができます。

依然として、アプリ全体の型チェックは、’next build’又は皆さんの使用されるエディターによって行われます。

Telemetry

Next.jsは約3年前にリリースされ、そのフレームワークは新たな機能やより優れた初期設定を含め、ここ3年で目まぐるしい成長を遂げてきました。

この改善プロセスは、人の手を駆使した、実に手作業的でした。

ZEITには、いくつかの大きなNext.jsアプリケーションがあります。 たとえば、 zeit.co, zeit.co/docs, と nextjs.org。私たちは、ZEITでNext.jsをドッグフーディングして、その経験に基づいてNext.jsを改良しました。

さらに、フィードバックを収集するためにコミュニティと積極的に連携してきました。読者の皆さんの中にも、それぞれの会社でどのようにNext.jsを使用されているかについて、Timと話をした人がいるのではないかと思います。

たとえば、カスタムサーバーを使用されているとか、カスタムwebpackを設定されているとか。このフィードバックは、Next.jsの機能開発において非常に貴重です。

ただし、このアプローチには一部のユーザーからのみフィードバックが得られるという問題があります。この一部のユーザーがかかえるニーズとユースケースは、皆さんと異なるかもしれません。

1つの例として、あまり通常な使用例ではありませんが、それなりの数のユーザーがCSSファイルをインポートすることを、next-css(又は、Sass/Less)かカスタム設定を通して行っています。このような使い方をしているユーザーの割合が分かれば、私たちはこれらの機能改良を優先的に行うことができます。

このため、Next.jsをさらに改良するために、より多くのフィードバックを収集する方法として、匿名の、より自動化されたアプローチを導入しました。

これにより、フレームワークに導入された改良がすべてのアプリケーションの底上げにつながっているか確認することが可能になります。

Telemetryの詳細については、nextjs.org / telemetry をご覧ください。 また、フィードバックに参加したくない場合は、オプトアウトする方法も書いてあります。


Build Feedback with Dots Indicating Activity

Next.jsのユーザーに聞き取りを行った際、’next build’が実行されている時、特に視覚的に何も起きていないように見えるという声が上がりました。

これを解決するために、next buildが実行されている際、コンソールに読み込みインジケーターが表示されるようにしました。このインジケーターが表示されることにより、コマンドがまだ実行中であり、プロセスがフリーズしていないことを視覚的にとらえることができます。

将来的には、このインジケーターを改良して、ビルドがどれくらい進んでいるのか、段階的に表示させるようにすることを計画しています。


f:id:commmune:20191016215330p:plain
The new build indication dots


Improved next/head Element Tracking

Next.jsは、アプリケーションのHTMLをレンダリングする<head>要素を管理する仕組みが組み込まれています。このAPIは、next / headモジュールを通じて公開されています。

たとえば、ページにタイトルを追加するには:


> import Head from 'next/head'
> 
> export default function MyPage() {
>   return (
>     <>
>       <Head>
>         <title>My Title</title>
>       </Head>
>       <h1>Hello World</h1>
>     </>
>   )
> }  


HTMLにレンダリングする場合、Next.jsは<Head>内でレンダリングされたすべてのコンポーネントを収集し、ページの<head>にタグをレンダリングします。

ただし、Next.jsでは、<Link>コンポーネントを使用して、シングルページアプリケーション(SPA)タイプのルート遷移を許可します。

<Link>をクリックすると、Next.jsはクライアント側でページをレンダリングするために必要なJavaScriptを取得します。 次に、ファイルに関連付けられたReactコンポーネントレンダリングします。

この遷移はクライアント側で発生するため、前のページから挿入された’’要素をクリーンアップする必要があります。そうしないと、古い要素が別のページにも表れてしまう可能性があります。

これまで、Next.jsは、<Head>によって提供された要素にクラス名を追加することにより、これらの要素を追跡していました。

上記の例をとると、<head>は次のようになります。


<head>
  <title class="next-head">My Title</title>
</head>


この方法は、next/headによって注入されたすべての要素がうまくタグ付けされていた点と容易にクリーンアップできた点から良く機能していました。

しかし、それぞれの要素に加えられた’class’属性により、第三者のサービスによって書き足されたスクリプトの認証がうまくいかないという問題を、一部のユーザーから問い合わせがきました。

そこで、Google ChromeチームのGerald Monacoは、要素にクラス名を加えずにクリーンアップ動作を維持する方法を思い付きました。

新しいアプローチは、(next / head)を通してレンダリングされた要素の数を保持する追加の<meta>タグを挿入することです。これにより、Next.jsはカウントを使用して既存の要素をクリーンアップできます。

結果として、このアプローチは、複数の要素がページの<head>に挿入される場合の初期HTML負荷を軽減します。

Preventing Non-Pages in the Pages Directory

Next.jsを使い始めるとき、最初にすることはpagesページディレクトリを作成することです。

慣例では、’pages’ディレクトリ内のすべてのファイルがアプリケーション内のルートになります。 簡単な例として、pages/ about.js/ about’になるということです。 時間が経つにつれ、ユーザーのアプリケーション(大小を問わず)のビルドパフォーマンスが低下するという報告が時折届きました。

さらに調査すると、これらのユーザーは’pages’ディレクトリにコンポーネント構造全体を作成したことが明らかになりました。

pagesディレクトリ内のすべてのファイルはページとして扱われるため、すべてのコンポーネントはアプリケーション内のページとしてコンパイルされます。これにより、ビルド時に多くのオーバーヘッドが発生し、無効なページに対して2つ以上のJavaScriptファイルが生成されてしまいます。

さらに、これはNext.jsがcode-split chunkを生成する基準にも影響を与えます。 これは、Next.jsがページ間でのライブラリの使用に関するヒューリスティックを使用しているためです。

このため、ユーザーがアプリケーションを作成する際、こうした落とし穴にはまらないようにする必要がありました。

現在、Next.js 9は、pagesディレクトリ内のファイルがReact Componentをエクスポートすることを検証するようになりました。

実際使用してみると、Next.jsは’pages’ディレクトリ内に潜在的なnon-pageが発見されたことを表示してくれます。

これにより、ユーザーはnon-pageを他のディレクトリに移動するように勧められます。結果として、開発、プロダクション、およびコード分割がより高速で正確になります。


Runtime Improvements

Next.jsフレームワークは多くの部分で構成されています。これらの1つは、クライアント側ランタイムです。 このランタイムは、hydration、クライアント側のルーティングなどを処理します。

この改良が焦点を当てたhydrationは、サーバーレンダリングまたはプリレンダリングされたHTMLをインタラクティブにするために必要なものです。 hydrationはイベントハンドラーを追加し、useEffect()componentDidMountなどのライフサイクルメソッドを呼び出します。これにより、エンドユーザーがアプリケーションを使用できるようになります。

さらに、Next.jsは基本的なhydration以上のものを処理します。たとえば、クライアント側ルーターのセットアップ、next / headの設定、next / dynamicを介した追加のアプリケーションロジックの読み込みなどです。

これらには、固有のランタイム部分もあります。

next / dynamicの場合、Next.jsはサーバー側でゆっくりとロードされたコンポーネントがクライアント側で準備されているか確認する必要があります。next / dynamicを使用するたびに、追加のJavaScriptバンドルが生成されます。これらのファイルは、hydration mismatchを避けるために、hydration前にロードされる必要があります。

以前、このランタイムは常にNext.jsランタイムバンドルに含まれていましたが、今はアプリケーション内にnext / dynamicが使用されている場合にのみ含まれます。これは、next / dynamicを使用しないアプリケーションに対してダウンロード、解析、実行されるJavaScriptが少なくなることを意味します。


AppTree Support

Reactエコシステムの一部のライブラリは、非常に具体的な方法でサーバー側レンダリングを実装しています。最たる例として、’getDataFromTree’と呼ばれるApolloのサーバーサイドレンダリングソリューションは、React tree及び各’Query’をレンダリングし、結果を待機してから、Reactツリーを再レンダリングします。

Next.jsは初期設定で、React treeにコンテキスト値を加えます。例えば、読み取り可能なルーターuseRouterで追加することです。

with-apolloの例で React treeをレンダリングする方法は、<App>レンダリングし、欠落しているプロパティをマニュアル入力することです。React Contextの追加により、コンテキストプロバイダーは別の要素であるため、これはできなくなりました。

Next.js 9.0.4から、getInitialPropsのコンテキストオブジェクトに’AppTree’という新しいプロパティが追加されました。 これは特に、ApollogetDataFromTreeのように外部ライブラリがReact tree全体を横切る必要がある場合などのために追加されました。

with-apolloは、変更を反映するように更新されました。 アプリに既にApolloを実装している場合、UseRouterおよびその他のAPIがNext.jsアプリケーションで動作するように、AppTreeを使ったアプローチに更新することをお勧めします。

Next.jsチームは、一般的にReact treeを横切ることを推奨していないため、Apolloやこれに似たライブラリーを使用されていない場合’AppTree’を使わないことを推奨いたします。 それは、React treeが1回のみならず、複数回レンダリングされるため、パフォーマンスに多くの負荷がかかってしまうからです。


Removal of the ‘next-server’ Package

1年ほど前にサーバーレス展開用にNext.jsの最適化を開始したとき、next-serverというパッケージを作成しました。 このパッケージは実験的であり、nextパッケージと一緒に公開されます。 どこにも正式に明文化されていませんが、これは可能な限り最小のNext.jsサーバーランタイムを実現する試みとして開発されました。

最終的に、パッケージは成功し、実稼働サーバーのランタイムが小さくなりました。 しかし、私たちは、Next.jsコンパイラーとstatic analysisを使用してランタイムをさらに小さくする革新的な方法を編み出しました。

これにより、next-serverは時代遅れになったため、[serverless target] (https://nextjs.org/docs#serverless-deployment)に置き換えられました。 このターゲットの出力は、next-serverパッケージをnextの代わりとして使用するよりもはるかに最適化されています。

このパッケージは時代遅れで、直接使用することも不可能だったので、いったんそのままにしてありました。理由としては、その一部が他のパッケージと共有された機能を有しており、その部分だけ切り離すためにはそれなりの人工が必要だったからです。

今回、私たちはこの人工をかけて、対象コードをnext-serverからnextパッケージに戻しました。これは、つまり、Next.jsフレームワークのすべてのコードは現在’next’パッケージに保存されているという意味です。

これにより、初心者もベテランのユーザーもNext.jsに貢献しやすくなりました。 現在、Single Compilation Process とUnified Build Configurationがあります。 以前は、コードがどのパッケージに属するかで、’next-server’と’next’とで個別の設定が存在しました。

Upgrading Next.js

プロジェクトが古いバージョンのNext.jsで実行されている場合、Next.js 9にアップグレードすることをお勧めします。

ほとんどの場合、アップグレードをするために何かしらの変更が必要になることはありません。 アップグレードガイドに従って、スムーズなアップグレードエクスペリエンスを確保できます。

ガイドが公開されてからアップデートに貢献してきたコミュニティのメンバーに感謝します。


What's Coming in the Future?

このブログ投稿された新たな最適化は、私たちが取り組んでいるより広範な最適化と機能拡張の始まりにすぎません。

進行中のRFCについては、近日中にアップデートを公開します。 それまでは、GitHubRFCラベルで一足早く覗き見ることができます。

これには、我々が研究してきたbuilt-in CSS support, public directory support, 及び src directory support等の機能が紹介されています。


Community

Next.jsコミュニティの継続的な成長を楽しみにしています。

800人以上のユーザーが少なくとも1度は貢献( 1 Commit)してくれました。 GitHubでは、41,100以上の星が付きました。

spectrum.chat/next-jsのNext.jsコミュニティは、最後のメジャーリリース以降、メンバーが倍になり、10,900人を超えるメンバーが登録しています。 一緒に参加しましょう!

コミュニティへの継続的な貢献と、企業やユーザーからの外部フィードバックに心が躍ります。