第三方指令碼會影響效能,因此請務必定期稽核,並使用高效的載入技術。本程式碼研究室說明如何最佳化第三方資源載入作業。其中涵蓋下列技巧:
延遲載入指令碼
延遲載入非關鍵資源
預先連線至必要來源
這個範例應用程式提供的是簡易網頁,其中有三項來自第三方來源的功能:
內嵌影片
用於呈現折線圖的資料視覺化程式庫
社群媒體分享小工具
一開始先評估應用程式效能,接著運用各項技巧提升應用程式效能。
評估效能
首先,請在全螢幕檢視畫面中開啟範例應用程式:
- 按一下「Remix to Edit」,讓專案可供編輯。
- 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 。
在網頁上執行 Lighthouse 效能稽核,以建立基準成效:
- 按下 `Control+Shift+J 鍵 (在 Mac 上為 Command+Option+J 鍵) 開啟開發人員工具。
- 按一下「Lighthouse」分頁標籤。
- 按一下「行動裝置」。
- 勾選「成效」核取方塊。(您可以清除「稽核」部分中的其他核取方塊)。
- 按一下「Simulated Fast 3G, 4x CPU Crashdown」。
- 勾選「清除儲存空間」核取方塊。
- 按一下「執行稽核」。
在電腦上執行稽核時,實際結果可能會有所差異,但您應注意首次內容繪製 (FCP) 的時間相當高,且 Lighthouse 建議有兩個調查機會:「排除會妨礙資源」以及「預先連線至必要來源」。(即使指標都是綠色,最佳化仍會產生改善)。
延遲第三方 JavaScript
我們在「排除會阻擋轉譯資源」稽核發現,可延後來自 d3js.org 的指令碼,因此可省下許多時間:
D3.js 是用來建立資料視覺化內容的 JavaScript 程式庫。範例應用程式中的 script.js
檔案會使用 D3 公用程式函式建立 SVG 折線圖,並附加到網頁中。這裡的作業順序很重要:剖析文件且載入 D3 程式庫後,script.js
就必須執行,因此會緊接在 index.html
的 </body>
結尾標記之前。
不過,D3 指令碼包含在網頁的 <head>
中,因而無法剖析其餘文件:
將剖析器加入指令碼標記時,有兩種魔術屬性可解除封鎖剖析器:
這個圖表對整個網頁來說並非必要,而且很有可能在需捲動位置,因此請使用 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>
延遲載入第三方資源
需捲動位置的所有資源都適合採用延遲載入。
範例應用程式在 iframe 中嵌入 YouTube 影片。如要查看網頁發出的要求數量,以及來自嵌入式 YouTube iframe 的要求數量,請按照下列步驟操作:
- 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 。
- 按下 `Control+Shift+J 鍵 (在 Mac 上為 Command+Option+J 鍵) 開啟開發人員工具。
- 按一下 [網路] 分頁標籤。
- 勾選「停用快取」核取方塊。
- 在「Throttling」下拉式選單中選取「Fast 3G」。
- 重新載入網頁。
在「Network」面板中,該網頁總共提出了 28 個要求,並傳輸了將近 1 MB 的壓縮資源。
如要識別 YouTube iframe
提出的要求,請在「Initiator」欄中尋找影片 ID 6lfaiXM6waw
。如何依網域將所有要求分組:
在「網路」面板中,在資料欄標題上按一下滑鼠右鍵。
在下拉式選單中選取「網域」欄。
如要依照網域排序要求,請按一下「網域」欄標題。
新的排序結果可顯示 Google 網域收到其他要求。整體而言,YouTube iframe 會為指令碼、樣式表、圖片和字型提出 14 個請求。但使用者必須向下捲動才能播放影片,但實際上並不需要這些素材資源。
只要將影片延遲載入,直到使用者向下捲動到網頁的該部分,這就會減少網頁一開始發出的要求數量。這個方法可以儲存使用者資料,並加快初始載入速度。
延遲載入的其中一種實作方法是使用 Intersection Observer,瀏覽器 API 會在元素進入或離開瀏覽器可視區域時收到通知。
步驟 1:禁止影片一開始載入
如要延遲載入影片 iframe,您必須先以正常的方式禁止載入影片 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-」開頭即可。
沒有 src
的 iframe 將無法載入。
步驟 2:使用 Intersection Observer 延遲載入影片
如要在使用者捲動至影片時載入影片,您必須瞭解發生時機。此時 Intersection Observer API 步驟就完成了。Intersection Observer API 可讓您註冊回呼函式,以便在要追蹤的元素進入或離開可視區域時執行。
如要開始,請建立新檔案並命名為 lazy-load.js
:
- 按一下「新增檔案」並輸入名稱。
- 按一下「新增這個檔案」。
將指令碼標記新增至文件標題:
<script src="/lazy-load.js" defer></script>
在 lazy-load.js
中建立新的 IntersectionObserver
,並向其傳遞要執行的回呼函式:
// create a new Intersection Observer
let observer = new IntersectionObserver(callback);
現在,請在 observe
方法中以引數的形式傳遞 observer
做為要觀看的目標元素 (在本例中為影片 iframe):
// 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
的布林值。
在這個範例中,target
是 iframe
。當 target
進入可視區域時,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);
});
});
- 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 。
- 按下 `Control+Shift+J 鍵 (在 Mac 上為 Command+Option+J 鍵) 開啟開發人員工具。
- 再按一下「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。這是很有意義的改善!
現在向下捲動頁面,並留意「網路」面板。取得影片後,你應該會看到頁面觸發其他要求。
預先連線至必要來源
您已延遲執行非關鍵 JavaScript 並延遲載入 YouTube 要求,現在可以該著手最佳化其餘的第三方內容。
在連結中加入 rel=preconnect
屬性,指示瀏覽器先與網域建立連線,再對該資源提出要求。這項屬性最適合用於提供特定網頁需求的來源。
您在「預先連線至必要來源」一文的建議步驟中所執行的 Lighthouse 稽核作業,您可以透過建立早期連線至 staticxx.facebook.com 和 youtube.com 的早期連線,這麼做可省下約 400 毫秒:
由於 YouTube 影片現採用延遲載入,僅離開 staticxx.facebook.com,也就是社群媒體分享小工具的來源。建立與這個網域的早期連線相當簡單,只要在文件的 <head>
中加入 <link>
標記即可:
<link rel="preconnect" href="https://staticxx.facebook.com">