這不是伺服器端轉譯,但仍想加快 React 網站的效能嗎?歡迎試用預先算繪功能!
react-snap
是第三方程式庫,用於將網站上的網頁預先轉譯為靜態 HTML 檔案。這樣可以改善應用程式中的首次繪製時間。
以下比較透過模擬 3G 連線和行動裝置載入的相同應用程式 (無論是否預先算繪):
這種報表有哪些優點?
大型單頁應用程式的主要效能問題是,使用者必須等待組成網站的 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 的樣式化頁面。
結論
如果您不是應用程式的伺服器端轉譯路徑,請使用 react-snap
向使用者預先轉譯靜態 HTML。
- 將其安裝為開發依附元件,並僅從預設設定開始。
- 如果網站可以正常運作,請使用實驗性的
inlineCss
選項來內嵌重要的 CSS。 - 如果您在任何路徑中的元件層級使用程式碼分割,請務必小心,不要預先轉譯載入狀態給使用者。
react-snap
README 檔案有詳細說明。