您不使用伺服器端算繪,但仍想加快 React 網站的效能嗎?試試預先算繪!
react-snap
是第三方程式庫,可將網站上的網頁預先轉譯為靜態 HTML 檔案。這麼做可以改善應用程式中的第一次繪圖時間。
以下比較在模擬 3G 連線和行動裝置上,有無預先算繪功能的相同應用程式:
這種報表有哪些優點?
大型單頁應用程式的主要效能問題,是使用者必須等待組成網站的 JavaScript 套件完成下載,才能查看任何實際內容。套件的大小越大,使用者就必須等待越久的時間。
為解決這個問題,許多開發人員會在伺服器上轉譯應用程式,而非只在瀏覽器上啟動應用程式。每次頁面/路徑轉換時,完整的 HTML 都會在伺服器上產生並傳送至瀏覽器,這雖然可縮短首次繪製時間,但會導致 Time to First Byte 變慢。
預先算繪是另一種技術,雖然不如伺服器算繪複雜,但也能改善應用程式中的首次顯示時間。在建構時間期間,系統會使用無頭瀏覽器 (或沒有使用者介面的瀏覽器) 產生每個路徑的靜態 HTML 檔案。接著,這些檔案可與應用程式所需的 JavaScript 套件一併發布。
react-snap
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 檔案做為酬載。您可以按一下 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 的樣式網頁。
結論
如果您未在應用程式中使用伺服器端轉譯路徑,請使用 react-snap
為使用者預先轉譯靜態 HTML。
- 將其設為開發依附元件,並從預設設定開始。
- 如果實驗性
inlineCss
選項適用於您的網站,請使用該選項內嵌重要 CSS。 - 如果您在任何路徑中使用元件層級的程式碼分割功能,請小心不要向使用者預先轉譯載入狀態。詳情請參閱
react-snap
README。