透過精細的分塊改善 Next.js 和 Gatsby 網頁載入效能

Next.js 和 Gatsby 中新版 Web Pack 分塊策略可盡量減少重複程式碼,以改善網頁載入效能。

Chrome 採用各種工具和協作功能 導入了 JavaScript 開放原始碼生態系統的架構最近有多項新的最佳化項目 ,改善 Next.js 的載入效能 「Gatsby」。本文介紹改良的精細分塊策略 ,現在這兩種架構皆預設為出貨。

簡介

和許多網路架構一樣,Next.js 和 Gatsby 使用 webpack 做為核心 Bundler。推出 Webpack 第 3 版 輸入 CommonsChunkPlugin,即可 在單一 (或少數)「常用」中,不同進入點共用的輸出模組區塊 (或 區塊)。共用程式碼可單獨下載,並提早儲存在瀏覽器快取中, 可以提高載入效能

許多單頁應用程式架構採用了一個進入點, 如下所示:

一般進入點和套件設定

雖然實際可行,但把所有共用的模組程式碼整合成單一區塊的概念則有其功用 至於並非每個進入點共用的模組,可下載至不使用該模組的路徑 導致下載的程式碼超過必要舉例來說,當 page1 載入時 common 區塊時,即使 page1 未使用 moduleC,它會載入 moduleC 的程式碼。 因此,Webpack v4 的外掛程式並採用 一個:SplitChunksPlugin

加強組塊

SplitChunksPlugin」的預設設定適用於多數使用者。多個分割區塊 根據多項條件建立 防止擷取多個路線的重複程式碼。

不過,許多使用這個外掛程式的網路架構仍會遵循「單一常見」分塊的方法 舉例來說,Next.js 會產生 commons 軟體包,其中包含在 在超過 50% 的網頁和所有架構依附元件 (reactreact-dom 等) 中使用。

const splitChunksConfigs = {
  …
  prod: {
    chunks: 'all',
    cacheGroups: {
      default: false,
      vendors: false,
      commons: {
        name: 'commons',
        chunks: 'all',
        minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
      },
      react: {
        name: 'commons',
        chunks: 'all',
        test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/,
      },
    },
  },

雖然將架構相依程式碼加入共用區塊中, 快取任何進入點,也就是將使用的常見模組加入超過 30 個 半個網頁的成效較差。如果修改這個比例,系統只會產生以下兩種結果:

  • 如果減少比率,下載出更多不必要的程式碼。
  • 如果您提高比率,則多條路線上會有更多程式碼。

為解決這個問題,Next.js 採用SplitChunksPlugin 設定 不必要的程式碼。

  • 所有規模夠大的第三方模組 (超過 160 KB) 都會分割成獨立的獨立模組 區塊
  • 系統會為架構依附元件 (reactreact-domframeworks 依此類推)
  • 您可以視需要建立共用區塊 (最多 25 個)
  • 要產生的區塊大小下限變更為 20 KB

這種精細的區塊化策略具有下列優點:

  • 縮短網頁載入時間。發出多個共用區塊,而非單一區塊 盡可能減少任何進入點不需要 (或重複) 的程式碼。
  • 改善導覽期間的快取功能。分割大型程式庫和架構依附元件 分為多個獨立區塊,可降低快取撤銷的可能性,因為兩者不太可能 直到升級為止

您可以在 webpack-config.ts 中查看 Next.js 採用的完整設定。

更多 HTTP 要求

SplitChunksPlugin 定義了精細分塊的基礎,並將此方法套用至 像 Next.js 這樣的架構並不是全新的概念然而,許多架構仍持續 使用單一經驗法則有幾個原因當中包含 HTTP 要求都可能會對網站效能造成負面影響。

瀏覽器只能向單一來源建立有限數量的 TCP 連線 (Chrome 為 6 個),因此 盡量減少套裝組合輸出的區塊數量,可確保要求總數 低於這個門檻不過,這個情況僅適用於 HTTP/1.1。HTTP/2 中的多工處理 可讓系統以單一連線,在單一連線時,並行串流多項要求 來源。換句話說,我們通常不需要擔心區塊數量 發出的訊息。

所有主要瀏覽器都支援 HTTP/2。Chrome 與 Next.js 團隊敬上 ,想看看能否分割 Next.js 的單一「commons」來增加要求數量區域組合 分為多個共用區塊,無論如何都會影響載入效能。他們一開始先測量 以及使用 maxInitialRequests敬上 資源。

要求數增加時的網頁載入效能

一個網頁上平均有三次測試 loadstart-render 改變初始顯示時間上限時,First Contentful Paint 都不會改變 要求數量 (從 5 至 15 個)。有趣的是,我們注意到應用程式效能負擔略微 必須積極分割成數百個要求

內含數百個請求的網頁載入效能

這證明瞭如果比率維持在可靠的門檻 (20 至 25 個要求) 內,就獲得足夠的平衡 介於載入效能與快取效率之間經過一些基準測試後,獲選為 25 項 maxInitialRequest 計數。

修改同時發生的請求數量上限,導致產生超過一次 共用套件,並且為每個進入點妥善分隔,大幅降低 相同網頁上不需要的程式碼

使用更多區塊來縮減 JavaScript 酬載

這項實驗只是為了修改請求數量,以便瞭解是否有 對網頁載入效能的負面影響結果顯示,將 maxInitialRequests 設為 測試頁面上的 25 是最佳選擇,因為這樣既能減少 JavaScript 酬載大小,又不會拖慢速度 下方。等待網頁保持可用狀態所需的 JavaScript 總數 這也解釋了為何網頁載入效能不見得能改善 或大量程式碼

webpack 以 30 KB 做為產生區塊的預設大小下限。不過,將 改為將 maxInitialRequests 值設為 25,大小下限為 20 KB,這樣才能提高快取效能。

透過精細區塊縮減大小

許多架構 (包括 Next.js) 都依賴用戶端轉送 (由 JavaScript 處理) 來注入 較新的指令碼標記。但他們如何在建構期間預先決定這些動態區塊?

Next.js 會使用伺服器端的建構資訊清單檔案,判斷要使用的輸出區塊 不同的進入點為一併提供這項資訊給用戶端,是經過橋接的用戶端 建構資訊清單檔案來對應每個進入點的所有依附元件。

// Returns a promise for the dependencies for a particular route
getDependencies (route) {
  return this.promisedBuildManifest.then(
    man => (man[route] && man[route].map(url => `/_next/${url}`)) || []
  )
}
Next.js 應用程式中多個共用區塊的輸出內容。

這項新的精細分段策略最初是在 Next.js 中推出,在標記背後經過測試, 早期採用者人數許多 JavaScript 整合讓他們使用的 JavaScript 總數大幅減少 整個網站:

網站 JS 總變化 差異百分比
https://www.barnebys.com/ -238 KB -23%
https://sumup.com/ -220 KB -30%
https://www.hashicorp.com/ -11 MB -71%
縮減所有路徑的 JavaScript 大小 (壓縮)

最終版本預設為 9.2 版

蓋斯比

Gatsby 用於遵循與用量導向的相同方法 定義常見模組的經驗法則:

config.optimization = {
  …
  splitChunks: {
    name: false,
    chunks: `all`,
    cacheGroups: {
      default: false,
      vendors: false,
      commons: {
        name: `commons`,
        chunks: `all`,
        // if a chunk is used more than half the components count,
        // we can assume it's pretty global
        minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
      },
      react: {
        name: `commons`,
        chunks: `all`,
        test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
      },

透過針對 Webpack 設定進行最佳化調整,來採用類似的精細分塊策略,他們也成功 我們發現,許多大型網站大幅縮減 JavaScript:

網站 JS 總變化 差異百分比
https://www.gatsbyjs.org/ -680 KB -22%
https://www.thirdandgrove.com/ -390 KB -25%
https://ghost.org/ -1.1 MB -35%
https://reactjs.org/ -80 KB -8%
縮減所有路徑的 JavaScript 大小 (壓縮)

請參閱 PR 以瞭解其運作方式 將這個邏輯實作至他們的 webpack 設定中,而在 v2.20.7 版中預設會發布該設定。

結論

運送精細區塊的概念並非 Next.js、Gatsby 或 Webpack 專用。適合所有人 建議改善應用程式的分段策略,只要遵循 組合方法。

  • 如果你想看到將相同的區塊化最佳化設定套用到香草 React 應用程式, 來看看這個 React 範本範例 app。該公式採用 簡化版精細分塊策略,有助您開始以 定義不同類型的邏輯
  • 以匯總作業來說,系統會依預設以精細的方式建立區塊。查看以下指標 manualChunks (如要手動新增) 設定行為