最佳化第三方 JavaScript

Milica Mihajlija
Milica Mihajlija

第三方指令碼會影響效能,因此請務必定期稽核,並使用有效的載入技術。本程式碼研究室將說明如何最佳化第三方資源的載入作業。涵蓋下列技巧:

  • 延後載入指令碼

  • 延遲載入非必要資源

  • 預先連結至必要來源

這個範例應用程式提供簡單的網頁,其中包含來自第三方來源的三項功能:

  • 影片嵌入

  • 用於算繪折線圖的資料視覺化程式庫

  • 社群媒體分享小工具

頁面的螢幕截圖,其中第三方資源已加以標示。
範例應用程式中的第三方資源。

您將先評估應用程式的效能,然後套用各項技巧,改善應用程式效能的各個層面。

評估成效

首先在全螢幕檢視畫面中開啟範例應用程式:

  1. 按一下「Remix to Edit」,即可編輯專案。
  2. 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕

在網頁上執行 Lighthouse 效能稽核,以建立基準效能:

  1. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  2. 按一下「Lighthouse」Lighthouse分頁標籤。
  3. 按一下「行動」
  4. 勾選「效能」核取方塊。(您可以清除「稽核」部分的其他核取方塊)。
  5. 按一下「模擬快速 3G,CPU 減速 4 倍」
  6. 勾選「清除儲存空間」核取方塊。
  7. 按一下「執行稽核」

在電腦上執行稽核時,具體結果可能會有所不同,但您應該會發現「首次顯示內容所需時間」(FCP) 的時間相當長,而 Lighthouse 會建議您調查兩個項目:移除會阻擋算繪作業的資源預先連線至必要的來源。(即使指標全都呈現綠色,最佳化做法仍可帶來改善效果)。

Lighthouse 稽核螢幕截圖,顯示 2.4 秒的 FCP,以及兩個改善機會:移除會阻斷轉譯的資源,以及預先連線至必要的來源。

延後第三方 JavaScript

排除會妨礙顯示的資源」稽核指出,您可以延遲來自 d3js.org 的指令碼,藉此節省一些時間:

排除會妨礙顯示的資源稽核畫面截圖,其中 d3.v3.min.js 指令碼已加以標示。

D3.js 是用於建立資料視覺化的 JavaScript 程式庫。範例應用程式中的 script.js 檔案會使用 D3 公用程式函式建立 SVG 折線圖,並附加至頁面。這裡的運算順序很重要:script.js 必須在剖析文件並載入 D3 程式庫後執行,因此才會在 index.html 中加入在結尾 </body> 標記之前。

不過,D3 指令碼包含在頁面的 <head> 中,因此會阻斷對其餘文件的剖析:

index.html 的螢幕截圖,其中標頭中顯示醒目顯示的指令碼標記。

新增至指令碼標記後,兩個魔法屬性可解除剖析器的封鎖:

  • async 可確保指令碼在背景下載,並在下載完成後盡快執行

  • defer 可確保在背景下載指令碼,並在剖析完成後執行。

由於這張圖表對整體網頁並非至關重要,且很可能會位於折疊下方,因此請使用 defer,確保沒有解析器阻擋。

步驟 1:使用 defer 屬性以非同步方式載入指令碼

index.html 的第 17 行,將 defer 屬性新增至 <script> 元素:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

步驟 2:確保運算順序正確

由於 D3 已延後,script.js 會在 D3 就緒前執行,導致發生錯誤。

具有 defer 屬性的指令碼會按照指定的順序執行。為確保 script.js 在 D3 就緒後執行,請將 defer 新增至其中,並將其移至文件的 <head> 中,緊接在 D3 <script> 元素之後。這項功能現在不會再阻斷剖析器,下載作業也會更快開始。

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

延遲載入第三方資源

位於折頁下方的所有資源,都是延遲載入的理想候選資源。

範例應用程式已將 YouTube 影片嵌入 iframe 中。如要查看網頁發出的請求數量,以及哪些請求來自嵌入的 YouTube iframe,請按照下列步驟操作:

  1. 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕
  2. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  3. 按一下 [網路] 分頁標籤。
  4. 勾選「Disable cache」核取方塊。
  5. 在「節流」下拉式選單中選取「快速 3G」
  6. 請重新載入頁面。

開發人員工具「網路」面板的螢幕截圖。

「Network」面板顯示,網頁總共發出 28 次請求,並傳輸了近 1 MB 的壓縮資源。

如要找出 YouTube iframe 提出的要求,請在「發起者」欄中尋找影片 ID 6lfaiXM6waw。如要將所有要求依網域分組:

  • 在「Network」面板中,按一下資料欄標題的滑鼠右鍵。

  • 在下拉式選單中選取「Domains」欄。

  • 如要依網域排序要求,請按一下「網域」欄標題。

新的排序方式顯示,有其他要求會傳送至 Google 網域。總計,YouTube iframe 會針對指令碼、樣式表、圖片和字型提出 14 項要求。不過,除非使用者實際捲動畫面播放影片,否則他們並不需要所有這些素材資源。

等到使用者向下捲動至網頁的該部分,再延遲載入影片,就能減少網頁最初發出的請求數量。這個做法可節省使用者資料,並加快初始載入速度。

實作延遲載入的其中一種方法,就是使用Intersection Observer,這是一種瀏覽器 API,可在元素進入或離開瀏覽器可視區域時通知您。

步驟 1:避免影片在初始載入

如要延後載入影片 iframe,您必須先避免以一般方式載入。方法是將 src 屬性替換為 data-src 屬性,以指定影片網址:

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src資料屬性,可讓您在標準 HTML 元素中儲存額外資訊。只要開頭為「data-」,資料屬性可以命名為任何名稱。

如果 iframe 沒有 src,就無法載入。

步驟 2:使用 Intersection Observer 延遲載入影片

如要在使用者捲動到影片時載入影片,您必須瞭解使用者捲動到影片的時間點。這時就輪到 Intersection Observer API 上場了。Intersection Observer API 可讓您註冊回呼函式,只要您要追蹤的元素進入或離開檢視區,就會執行這項函式。

如要開始使用,請建立新檔案並將其命名為 lazy-load.js

  • 按一下「New File」,然後為檔案命名。
  • 按一下「新增此檔案」

在文件標頭中加入指令碼標記:

 <script src="/lazy-load.js" defer></script>

lazy-load.js 中建立新的 IntersectionObserver,並傳遞回呼函式來執行:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

接著,請將 observer 的目標元素 (在本例中為影片 iframe) 做為 observe 方法中的引數傳遞,以便讓 observer 監控該元素:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback 會接收 IntersectionObserverEntry 物件清單和 IntersectionObserver 物件本身。每個項目都包含 target 元素和屬性,用來描述其尺寸、位置、進入檢視區塊的時間等。IntersectionObserverEntry 的其中一個屬性是 isIntersecting,這是一個布林值,在元素進入可視區域時等於 true

在本例中,targetiframetarget 進入可視區域時,isIntersecting 等於 true。如要實際查看,請將 callback 替換為下列函式:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕
  2. 按下 `Control + Shift + J` 鍵 (在 Mac 上為 `Command + Option + J` 鍵) 開啟開發人員工具。
  3. 再按一下「Console」(控制台) 分頁標籤即可。

請試著上下捲動。您應該會看到 isIntersecting 的值變更,以及控制台中記錄的目標元素。

如要在使用者捲動至影片位置時載入影片,請使用 isIntersecting 做為條件來執行 loadElement 函式,該函式會從 iframe 元素的 data-src 取得值,並將其設為 iframe 元素的 src 屬性。這個替換動作會觸發影片載入作業。接著,在影片載入後,請對 observer 呼叫 unobserve 方法,停止觀看目標元素:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

步驟 3:重新評估成效

如要查看資源大小和數量有何變化,請開啟開發人員工具的「Network」面板,然後重新載入網頁。Network 面板顯示網頁發出 14 次要求,且只有 260 KB。這項改進相當實用!

現在請向下捲動頁面,並留意「Network」面板。當您前往影片時,應該會看到網頁觸發其他要求。

預先連結必要來源

您已延遲非關鍵的 JavaScript,並延後載入 YouTube 請求,現在是時候最佳化剩餘的第三方內容。

rel=preconnect 屬性新增至連結,可讓瀏覽器在提出資源要求前,先建立與網域的連線。這項屬性最適合用於提供網頁所需資源的來源。

在「預先連線至必要來源」一節中,我們建議您在第一步執行 Lighthouse 稽核,這樣您就能建立早期連線,節省約 400 毫秒的時間,連線至 staticxx.facebook.com 和 youtube.com:

預先連結必要來源稽核項目,其中醒目顯示 staticxx.facebook.com 網域。

由於 YouTube 影片現在採用延遲載入,因此只會載入社群媒體分享小工具的來源 staticxx.facebook.com。只要在文件的 <head> 中加入 <link> 標記,即可建立與此網域的早期連線:

  <link rel="preconnect" href="https://staticxx.facebook.com">

重新評估成效

以下是最佳化後的網頁狀態。請按照程式碼研究室「評估效能」部分的步驟,執行另一項 Lighthouse 稽核作業。

Lighthouse 稽核顯示首次顯示內容所需時間為 1 秒,效能分數為 99。