メインコンテンツへ移動

フロントエンドレンダリングモードの探求

無料2020-06-14#Front-End#SSR vs Prerendering#预渲染与SSR#服务端渲染#前后端混合渲染#Universal Rendering#Rehydration

CSR、SSR、Prerendering、Rehydration はそれぞれどういう意味か?

前置き

React、Vue などのモダンなフロントエンドフレームワークの旗印の下、CSR(Client-Side Rendering)モードが深く人々の心に浸透しています:

CSR (Client-Side Rendering) - rendering an app in a browser, generally using the DOM.

フロントエンド部分はほぼすべてクライアント側で動的にレンダリングされています(クライアント側で JS コードを実行し、動的に DOM 構造を作成)。例えば:

<!DOCTYPE html>
<html>
  <head>
    <title>My Awesome Web App</title>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="app"></div>
    <script src="bundle.js"></script>
  </body>
</html>

クライアント側のロジックが重くなるほど、初期化時に実行する JS が多くなり、ファーストスクリーンのパフォーマンスが遅くなるため、より多くのレンダリングモードの探求が現れました:

  • SSR(Server-Side Rendering):サーバー側レンダリング。サーバー側で Web アプリケーションを HTML にレンダリング

  • Rehydration:二次レンダリング。サーバー側でレンダリングされた HTML DOM 構造とデータを再利用し、クライアント側で JS レンダリングを「ウォームスタート」

  • Prerendering:プレレンダリング。コンパイル時にクライアントアプリケーションを実行して初期状態を取得し、静的 HTML を生成

一.CSR

CSR(Client-side rendering)、つまりクライアント側レンダリングは、JS で直接在ブラウザでページをレンダリングすることを指し、データリクエスト、ビューテンプレート、ルーティングを含むすべてのロジックがクライアント側で処理されます

Client-side rendering (CSR) means rendering pages directly in the browser using JavaScript. All logic, data fetching, templating and routing are handled on the client rather than the server.

レンダリングフローは下図の通り:

P.S.その中に現れる FCP と TTI は 2 つの重要なパフォーマンス指標です:

  • FCP(First Contentful Paint):ユーザーがリクエストしたコンテンツが画面上で可視になる時点

  • TTI(Time To Interactive):ページがインタラクティブになる時点

主な欠点は、アプリケーションの更新イテレーションに伴い、クライアント側で実行する JS コード量がますます多くなる ことで、事前のサードパーティライブラリ/フレームワーク、polyfill などが一定の程度でファーストスクリーンのパフォーマンスを遅くし、(中低端)モバイルデバイスで特に顕著です

Code splittinglazy-load などの最適化措置で一部緩和できますが、最適化スペースは比較的限られており、根本的に問題を解決する役には立ちません

この時、レンダリングモードを変えることだけがより多くの可能性を創造できます

二.SSR

SSR(Server-Side Rendering)は特に新しい概念ではなく、前後端分层之前的長い期間ではサーバー側レンダリングが主流でした(JSP、PHP)。サーバー側で完全な HTML ページを生成:

Server rendering generates the full HTML for a page on the server in response to navigation.

クライアント側の二次リクエストデータのネットワークオーバーヘッドと、ビューテンプレートをレンダリングするパフォーマンス負担を省きます。CSR と比較して、その FCP、TTI は通常より速くなります:

P.S.一方で、サーバー側のネットワーク環境はクライアント側より優れており、内部サーバー間通信パスもより短いです

ページロジック(即時データリクエストを含む)とテンプレートレンダリング作業がサーバー側で完了するため、クライアント側の JS コード量が削減され、ストリーミングドキュメント解析(streaming document parsing)などのブラウザ最適化メカニズムもその役割を発揮でき、低端デバイスと弱網状況でより良いパフォーマンスを発揮します。しかしサーバー側でページを生成するのにも時間が必要で、ページコンテンツレスポンスタイム(TTFB, Time to First Byte)が遅くなる原因になります

一つの解決策は、ストリーミング SSR、[コンポーネントレベルキャッシュ](https://medium.com/ @reactcomponentcaching/speedier-server-side-rendering-in-react-16-with-component-caching-e8aa677929b1)、テンプレート化HTML キャッシュ などの技術でさらに最適化することです

もう一つの解決策はレンダリングモード上で探求を続け、静的レンダリング(Static Rendering)を採用することです

三.Static Rendering

HTML ページを生成する作業をコンパイル時に置き、リクエストが来た時に動的に完了する必要はありません。各 URL に対して個別に HTML ファイルを事前生成し、さらに CDN を活用してアクセスを加速:

Static rendering happens at build-time and offers a fast First Paint, First Contentful Paint and Time To Interactive - assuming the amount of client-side JS is limited. Unlike Server Rendering, it also manages to achieve a consistently fast Time To First Byte, since the HTML for a page doesn't have to be generated on the fly.

レンダリングフローは下図の通り:

P.S.SSR 第一部の Server Rendering レンダリング作業が Streaming で静的 HTML ファイルを配信することに変わります

静的レンダリングも完璧ではなく、その重要な問題は*「静的」*にあります:

  • 各 URL に対して個別に HTML ファイルを生成する必要がある:すべての可能な URL を事前に知ることができない、または大量の異なるページが存在するサイトの場合、静的レンダリングは容易ではなく、甚至根本的に実行不可能

  • 静的コンテンツ向き:動的、パーソナライズされたコンテンツには効果が大きくない

さらに、静的レンダリングと似た概念に、プレレンダリング(Prerendering)があります

Prerendering

主な違いは、静的レンダリングで得られたページはすでにインタラクティブで、クライアント側で大量の JS コードを 추가로実行する必要がないのに対し、プレレンダリングはクライアント側レンダリングを経て初めて真にインタラクティブになる ことです:

Static rendered pages are interactive without the need to execute much client-side JS, whereas prerendering improves the First Paint or First Contentful Paint of a Single Page Application that must be booted on the client in order for pages to be truly interactive.

つまり、JS を無効にした後、静的レンダリングのページはほとんど影響を受けませんが、プレレンダリングのページはハイパーリンクなどの基本機能だけが残ります

四.Rehydration

Rehydration モードは CSR と SSR を組み合わせたもので、サーバー側で基本内容をレンダリングした後、クライアント側で二次レンダリング(Rehydration)を行います

Often referred to as Universal Rendering or simply "SSR", this approach attempts to smooth over the trade-offs between Client-Side Rendering and Server Rendering by doing both. Navigation requests like full page loads or reloads are handled by a server that renders the application to HTML, then the JavaScript and data used for rendering is embedded into the resulting document.

例えば:

実際のレンダリングフローは以下の通り:

注意bundle.js仍然是全量の CSR コードで、これらのコードが実行完了して初めてページが真にインタラクティブになります。したがって、このモードでは、FP(First Paint)は向上しますが、TTI(Time To Interactive)は遅くなる可能性があります。クライアント側で二次レンダリングが完了する前に、ページはユーザー入力にレスポンスできないためです(JS コードの実行でブロックされます)

二次レンダリングによるインタラクションがレスポンスできない問題に対して、可能な最適化方向はインクリメンタルレンダリング(例えば React Fiber)、およびプログレッシブレンダリング/部分レンダリング です

Trisomorphic Rendering

[Service Worker](/articles/理解 web-workers/) も考慮に入れると、三者涉及のレンダリングモード もあります:

SSR + CSR + ServiceWorker rendering = Trisomorphic Rendering

下図の通り:

まずストリーミング SSR で初期ページをレンダリングし、次に Service Worker がルーティングルールに基づき、SSR を活用してターゲット HTML ページをレンダリング:

It's a technique where you can use streaming server rendering for initial/non-JS navigations, and then have your service worker take on rendering of HTML for navigations after it has been installed. This can keep cached components and templates up to date and enables SPA-style navigations for rendering new views in the same session.

主な利点は、三者間でテンプレートレンダリングとルーティング制御ロジックを共有できることです:

This approach works best when you can share the same templating and routing code between the server, client page, and service worker.

五.まとめ

各レンダリングモードには一定の利点があり、その限界と欠点もあります。実際のシーンでは多種の要因の下で权衡して選択する必要があります:

参考資料

コメント

コメントはまだありません

コメントを書く