使用因應回應預先算繪路徑

這不是伺服器端轉譯,但仍想加快 React 網站的效能嗎?歡迎試用預先算繪功能!

react-snap 是第三方程式庫,用於將網站上的網頁預先轉譯為靜態 HTML 檔案。這樣可以改善應用程式中的首次繪製時間。

以下比較透過模擬 3G 連線和行動裝置載入的相同應用程式 (無論是否預先算繪):

並排載入的比較項目。使用預先算繪的版本載入時間加快 4.2 秒。

這種報表有哪些優點?

大型單頁應用程式的主要效能問題是,使用者必須等待組成網站的 JavaScript 套件完成下載,才能看到任何實際內容。套裝組合越大,使用者等待的時間就越長。

為解決這個問題,許多開發人員採取的方式是在伺服器上轉譯應用程式,而不是只在瀏覽器中啟動應用程式。每次轉換頁面/路徑時,系統都會在伺服器上產生完整的 HTML 並傳送至瀏覽器,這樣可以減少首次繪製時間,但也會降低第一個位元組時間的成本。

「預先轉譯」是獨立技術,較伺服器轉譯來說較不複雜,但也提供可以改善應用程式中的首次繪製時間的方法。在建構時間期間,系統會使用無使用者介面瀏覽器或沒有使用者介面的瀏覽器,產生每條路徑的靜態 HTML 檔案。然後,這些檔案可與應用程式所需的 JavaScript 套件一起傳送。

回應小睡

react-snap 使用 Puppeteer 在應用程式中建立不同路徑的預先轉譯 HTML 檔案。首先,請將其安裝為開發依附元件:

npm install --save-dev react-snap

然後在 package.json 中新增 postbuild 指令碼:

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

這樣每當建構應用程式 (npm build) 時,系統就會自動執行 react-snap 指令。

最後,您需要變更應用程式的啟動方式。將 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 要求的網址,然後按一下 Chrome 開發人員工具中的「Previews」分頁標籤,即可查看 HTML 酬載的內容。

前後對照比較。後圖顯示內容完成轉譯。

閃爍未加上樣式的內容

雖然靜態 HTML 現在可以立即算繪,但依預設仍不會設定樣式,這可能會導致顯示「未套用樣式的內容」(FOUC) 的問題。如果您使用 CSS-in-JS 程式庫產生選取器,就特別需要留意這一點,因為 JavaScript 組合必須先完成執行,才能套用任何樣式。

為避免發生這種狀況,「重要的」CSS 或初始頁面轉譯所需的 CSS 數量下限,可以直接內嵌在 HTML 文件的 <head> 中。react-snap 實際上會使用另一個第三方程式庫 (minimalcss) 擷取不同路徑的任何重要 CSS。您可以在 package.json 檔案中指定以下內容來啟用這項功能:

"reactSnap": {
  "inlineCss": true
}

現在看看 Chrome 開發人員工具的回應預覽畫面,結果預覽會顯示內含重要 CSS 的樣式化頁面。

前後對照比較。下方圖中顯示的內容已經過內嵌,且因內嵌重要的 CSS 而調整樣式。

結論

如果您不是應用程式的伺服器端轉譯路徑,請使用 react-snap 向使用者預先轉譯靜態 HTML。

  1. 將其安裝為開發依附元件,並僅從預設設定開始。
  2. 如果網站可以正常運作,請使用實驗性的 inlineCss 選項來內嵌重要的 CSS。
  3. 如果您在任何路徑中的元件層級使用程式碼分割,請務必小心,不要預先轉譯載入狀態給使用者。react-snap README 檔案有詳細說明。