第三方指令碼會影響成效,因此請務必定期稽核,並使用高效率的載入技術來載入指令碼。本程式碼研究室將說明如何最佳化第三方資源的載入。課程中涵蓋下列技術:
正在延後載入指令碼
延遲載入非重要資源
預先連線至必要來源
隨附的範例應用程式中有簡單網頁,以及三項來自第三方來源的功能:
嵌入影片
用於轉譯折線圖的資料視覺化程式庫
社群媒體分享小工具
![頁面螢幕截圖,特別框出第三方資源。](https://web.dev/static/articles/codelab-optimize-third-party-javascript/image/screenshot-the-page-thi-cc10c15b4c9aa.png?authuser=19&hl=zh-tw)
第一步是評估應用程式成效,並運用各項技巧改善應用程式效能的不同面向。
評估成效
首先,在全螢幕檢視畫面中開啟範例應用程式:
- 按一下「Remix to Edit」即可編輯專案。
- 如要預覽網站,請按下「查看應用程式」。然後按下
全螢幕
。
在網頁上執行 Lighthouse 效能稽核,建立基準成效:
- 按下 Control+Shift+J 鍵 (或在 Mac 上為 Command+Option+J 鍵) 開啟開發人員工具。
- 按一下「Lighthouse」分頁標籤。
- 按一下「行動裝置」。
- 勾選「成效」核取方塊。(您可以取消勾選「稽核」部分的其他核取方塊)。
- 點選「Simulated Fast 3G, 4x CPU Slowdown」。
- 勾選「Clear Storage」(清除儲存空間) 核取方塊。
- 按一下「執行稽核」。
在電腦上執行稽核時,實際結果可能會有所不同,但您應留意首次顯示內容所需時間 (FCP) 時間相當長,而且 Lighthouse 建議調查兩項機會:排除禁止轉譯資源和預先連線至必要來源。(即使指標都顯示為綠色,但最佳化功能仍能改善成效)。
![Lighthouse 稽核的螢幕截圖,顯示 2.4 秒的 FCP 和兩項商機:排除禁止轉譯資源和預先連線必要來源。](https://web.dev/static/articles/codelab-optimize-third-party-javascript/image/screenshot-lighthouse-au-d025b777206eb.png?authuser=19&hl=zh-tw)
延遲第三方 JavaScript
「排除禁止轉譯資源」稽核發現,您可以將 d3js.org 的指令碼延後,藉此節省時間:
![醒目顯示 d3.v3.min.js 指令碼的螢幕截圖,「排除禁止轉譯資源」的螢幕截圖。](https://web.dev/static/articles/codelab-optimize-third-party-javascript/image/screenshot-eliminate-ren-e12e8b7ae1ab3.png?authuser=19&hl=zh-tw)
D3.js 是一個 JavaScript 程式庫,可用來建立資料圖表。範例應用程式中的 script.js
檔案會使用 D3 公用程式函式建立 SVG 折線圖,並附加到頁面中。這裡的作業順序相當重要:script.js
必須在文件剖析及載入 D3 程式庫後執行,因此就在 index.html
的 </body>
結尾標記之前。
不過,D3 指令碼包含在網頁的 <head>
中,導致系統剖析其餘文件:
![index.html 的螢幕截圖,有醒目顯示的指令碼標記。](https://web.dev/static/articles/codelab-optimize-third-party-javascript/image/screenshot-indexhtml-h-4a8c4790995c8.png?authuser=19&hl=zh-tw)
將剖析器新增至指令碼標記時,可以運用以下兩個魔法屬性來解除封鎖剖析器:
這個圖表對整體網頁來說並不重要,而且很可能是需捲動位置,因此請使用 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
屬性的指令碼會按照指定順序執行。為確保在 D3 準備就緒後執行 script.js
,請將其加入 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」。
- 重新載入網頁。
「網路」面板顯示該頁面共提出 28 個要求,並傳輸將近 1 MB 的壓縮資源。
如要找出 YouTube iframe
提出的要求,請在「申請者」欄中尋找影片 ID 6lfaiXM6waw
。如何依網域將所有要求分組:
在「Network」面板中,在資料欄標題上按一下滑鼠右鍵。
在下拉式選單中,選取「Domains」欄。
如要按照網域排序要求,請按一下「網域」欄標題。
新的排序顯示,還有許多 Google 網域提出的要求。共計 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:重新評估成效
如要查看資源大小和數量的變化情形,請開啟開發人員工具的「網路」面板,然後再次重新載入頁面。「網路」面板顯示頁面提出 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">
重新評估成效
以下是最佳化後的網頁狀態。請按照程式碼研究室「評估效能」一節中的步驟進行,再次執行 Lighthouse 稽核。