リアクション スナップを使用したプリレンダリング ルート

サーバーサイド レンダリングではないけれども、React サイトのパフォーマンスを向上させたい場合は、プリレンダリングを試す

react-snap は、サイトのページを静的 HTML ファイルにプリレンダリングするサードパーティ ライブラリです。これにより、アプリの First Paint の時間を短縮できます。

同じアプリで、シミュレートされた 3G 接続とモバイル デバイスでプリレンダリングが読み込まれる場合としない場合の比較を以下に示します。

横並びで読み込みの比較。プリレンダリングを使用したバージョンでは、読み込みが 4.2 秒速くなります。

メリット

大規模なシングルページ アプリケーションのパフォーマンスに関する主な問題は、サイトを構成する JavaScript バンドルのダウンロードが完了するのを待たないと、実際のコンテンツが表示されないことです。バンドルが大きいほど、ユーザーの待ち時間が長くなります。

多くのデベロッパーは、この問題を解決するために、ブラウザ上で起動するのではなく、サーバー上でアプリをレンダリングするアプローチを採用しています。ページまたはルートが遷移するたびに、完全な HTML がサーバーで生成され、ブラウザに送信されます。これにより、First Paint の時間は短縮されますが、最初のバイトまでの時間は遅くなります。

プリレンダリングはサーバー レンダリングよりも複雑ではない別の手法ですが、アプリの First Paint 時間を改善する手段でもあります。ヘッドレス ブラウザ、またはユーザー インターフェースのないブラウザを使用して、ビルド時に各ルートの静的 HTML ファイルを生成します。これらのファイルは、アプリケーションに必要な JavaScript バンドルと一緒に配布できます。

リアクション スナップ

react-snapPuppeteer を使用して、アプリ内のさまざまなルートのプリレンダリングされた HTML ファイルを作成します。まず、開発の依存関係としてインストールします。

npm install --save-dev react-snap

次に、package.jsonpostbuild スクリプトを追加します。

"scripts": {
  //...
  "postbuild": "react-snap"
}

これにより、アプリケーションの新しいビルドが作成されるたびに react-snap コマンドが自動的に実行されます(npm build)。

最後に、アプリの起動方法を変更する必要があります。src/index.js ファイルを次のように変更します。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

ReactDOM.render を使用してルートの React 要素を DOM に直接レンダリングするのではなく、子ノードがすでに存在するかどうかを確認し、HTML コンテンツがプリレンダリング(またはサーバーでレンダリング)されたかどうかを確認します。その場合は、ReactDOM.hydrate を使用して、イベント リスナーを新しく作成するのではなく、作成済みの HTML にアタッチします。

アプリケーションをビルドすると、クロールされる各ルートのペイロードとして静的 HTML ファイルが生成されるようになります。HTML ペイロードがどのように表示されるかを確認するには、HTML リクエストの URL をクリックし、Chrome DevTools の [プレビュー] タブをクリックします。

使用前と使用後の比較。アフター ショットは、コンテンツがレンダリングされたことを示します。

スタイル設定されていないコンテンツのフラッシュ

静的 HTML はほぼ即座にレンダリングされるようになりましたが、依然としてデフォルトでスタイル設定が行われていないため、「スタイル設定されていないコンテンツのフラッシュ」(FOUC)が表示される可能性があります。これは特に、CSS-in-JS ライブラリを使用してセレクタを生成している場合に顕著になります。これは、スタイルを適用する前に JavaScript バンドルの実行を終了する必要があるためです。

これを防ぐために、重要な CSS、つまり最初のページをレンダリングするために必要な最小限の CSS を、HTML ドキュメントの <head> に直接インライン化できます。react-snap は内部で別のサードパーティ ライブラリ(minimalcss)を使用して、さまざまなルートで重要な CSS を抽出します。これを有効にするには、package.json ファイルで次のように指定します。

"reactSnap": {
  "inlineCss": true
}

Chrome DevTools のレスポンス プレビューを見ると、重要な CSS がインラインでスタイル化されたページが表示されています。

使用前と使用後の比較。アフター ショットでは、重要な CSS をインライン化してコンテンツがレンダリングされ、スタイルが設定されています。

まとめ

アプリでサーバーサイド レンダリング ルートを使用しない場合は、react-snap を使用して静的 HTML をユーザーに事前レンダリングします。

  1. 開発の依存関係としてインストールし、デフォルト設定のみから始めます。
  2. サイトに適している場合は、試験運用版の inlineCss オプションを使用して重要な CSS をインライン化します。
  3. ルート内のコンポーネント レベルでコード分割を使用する場合は、読み込み状態をユーザーに事前レンダリングしないように注意してください。詳細については、react-snap README をご覧ください。