內容腳本是你的副檔名注入網頁的 JavaScript 檔案,並且會在這些網頁的上下文中執行。 透過內容腳本,你的擴充功能可以透過讀取或更改 DOM 來存取並修改已渲染的網頁。
然而,內容腳本對網頁效能有明顯影響,例如減慢頁面載入速度。 如果內容腳本在頁面載入時執行大量程式碼,這種情況就可能發生。
本文提供最佳實務,幫助你將擴充功能對用戶造訪的網頁效能影響降到最低。
為您的擴充內容腳本建立設定
要分析你擴充功能內容腳本的效能,請使用 Microsoft Edge DevTools 或 Edge 追蹤工具,詳情見以下章節。
使用 Microsoft Edge DevTools 來設定你的內容腳本
DevTools 提供一組用於檢查、除錯及分析網頁所使用的程式碼的功能。 DevTools 也可以用來分析你擴充功能的程式碼。
在本節中,您將學習如何使用 DevTools 中的 效能 工具來分析擴充功能的內容腳本。 欲了解更多 效能工具, 請參閱 「分析執行時效能」 (教學) 。
要開啟 DevTools,請右鍵點擊網頁,然後選擇 檢查。 或者,在 Windows、Linux) 按 Ctrl+Shift+I (macOS) 按 Command+Option+I (。 DevTools 開啟。
在 DevTools 的 活動列中,選擇 效能 (
) 分頁。如果該分頁看不到,請選擇 「更多工具 」 (
) >效能。要開始錄製表演檔案,請點擊 「錄製 」
) 按鈕。重新載入頁面以擷取與頁面載入時間對應的剖析資料,然後頁面載入完成後,點擊 停止
) 按鈕結束錄影。 DevTools 顯示錄製的效能剖面:
要搜尋由你的內容腳本引起的效能事件,Windows/Linux 按 Ctrl+F,macOS 按 Command+F 。 搜尋文字框會出現在效能工具的底部。
輸入 Evaluate 腳本 ,然後按下 Enter 鍵,直到 效能 工具勾勒出由你的內容腳本引起的效能事件。 當你在摘要面板的腳本標籤顯示內容腳本名稱時,就知道你找到了正確的表演事件:
使用邊緣追蹤工具來分析你的內容腳本
邊緣追蹤工具(網址) edge://tracing 是一個強大的工具,能提供擴充功能的詳細分析。 在本節中,您將學習如何使用邊緣描摹工具,了解擴充功能對頁面載入時間的影響。 想了解更多基於 Perfetto 工具的追蹤工具,請參閱 Perfetto 追蹤文件中的 Perfetto UI 。
要開啟邊緣追蹤工具,請開啟一個新分頁或視窗,然後前往
edge://tracing。 追蹤介面打開了。要開始新的追蹤,請在工具左上角點擊 「記錄 」按鈕。 記錄 一個新的追蹤 對話框會開啟。
選擇 「手動選擇設定 」選項按鈕。 分類列表會出現。
若要擷取擴充功能內容腳本編譯與執行的詳細資訊,請選擇以下所有類別:
- 擴展
- V8
- 開發工具
- devtools.timeline
點擊 錄製 按鈕。 對話框關閉,邊緣追蹤工具開始記錄追蹤。
開啟一個新分頁,載入你的擴充功能影響的網頁。 追蹤工具會收集有關你擴充功能對網頁效能影響的數據。
打開邊緣追蹤工具正在執行的分頁,然後點擊 停止 按鈕。 新的追蹤資訊會顯示在工具中。
篩選結果
Edge 追蹤工具記錄的痕跡提供了大量關於瀏覽器及擴充功能的資訊。
要篩選資訊,只顯示與你擴展影響的網頁相關內容:
在頁面
edge://tracing中,按 Shift+Esc 開啟 瀏覽器工作管理員 對話框。在 瀏覽器工作管理員 對話框中,搜尋對應你擴充功能影響的網頁分頁,並標註程序 識別 欄位中的數字。 關閉對話方塊。
在邊緣追蹤工具的工具列中,點選 「流程」,然後選擇對應你標記的流程 ID 的勾選框。 清除所有其他勾選框。
在邊緣追蹤工具的右上角,點選搜尋欄,輸入 ScriptInjection::InjectJS,然後反覆按 Enter 鍵,直到底部面板標示出與你的擴充功能對應的事件。
底部面板顯示活動開始時間及總時長:
關注重要事件
要繼續分析你擴充功能內容腳本在網頁上的效能影響,請在 ScriptInjection::InjectJS 事件中尋找以下關鍵事件:
- v8.compile - 顯示內容腳本的編譯時間。
- v8.run - 表示編譯後腳本的執行時間。
只加入擴充功能所需的內容腳本程式碼
你的擴充功能內容腳本會在網頁的上下文中執行。 為了減少內容腳本對該網頁的影響,務必只在內容腳本中加入擴充功能在網頁上下文中執行所需的最小程式碼。 審核內容腳本中的程式碼,移除舊有框架、工具、函式庫或其他程式碼,這些程式碼在 Microsoft Edge 中執行時並不必要。
你可以利用懶惰載入和程式碼拆分技巧,減少內容腳本中執行的程式碼量:
懶載入 是指根據使用者操作、頁面內容或擴充邏輯,只在需要時才載入程式碼的過程。
程式碼拆分 是指將程式碼分割成較小的區塊或模組,這些模組可以單獨載入或按需載入。
如果你的擴充功能夠小,就不需要建置工具來拆分程式碼。 如果你的擴充功能較大,且程式碼較複雜管理,使用建置工具將程式碼拆分成較小的區塊。 建置工具可以幫助你將程式碼組織成邏輯單元,這些單元可以隨時載入。 例如,你可以使用 webpack 將程式碼拆分為 入口點 和 動態匯入:
每次頁面載入都會載入入口點。
動態匯入僅在需要時載入,例如使用者與網頁或擴充功能的使用者介面互動時:
// When the user clicks on the page. document.addEventListener("click", async () => { // Dynamically load the code that's needed to handle the click event. const module = await import("chunk.js"); // Do something with the newly loaded module code. });
只在指定的頁面和幀中載入內容腳本
你的擴充功能不必在使用者造訪的每個網頁上執行。 為了減少網頁載入時執行的程式碼量,請設定擴充功能只在需要的內容腳本和框架上載入。
要配置內容腳本載入的頁面與框架,請在副檔名清單檔案中使用 matches 該區段的屬性 content_scripts 定義 URL 模式。 欲了解更多,請參閱 Chrome 擴充功能文件中的「在內容腳本中注入靜態宣告」。
你也可以使用 chrome.scripting 擴充功能 API,將內容腳本程式化地注入網頁。 這個 API 允許你根據使用者的動作、網頁內容或擴充邏輯注入內容腳本。 想了解更多,請參閱 Chrome 擴充功能文件中的 chrome.scripting 。
在設定內容腳本載入位置時,請遵循以下最佳實務:
在你的擴充名清單檔案中,盡量使用最具體的 URL 模式來處理
matches和exclude_matches屬性。 例如,如果你的內容腳本只需要在 example.com 網域的網頁上執行,請用https://example.com/*「代替」。*://*/*要控制內容腳本是只在頂層框架執行,還是同時在符合 URL 模式的巢狀框架中執行,請使用副檔名清單檔案中的該
all_frames屬性。預設情況下,
all_frames是false,這表示你的內容腳本只會在頂層框架中執行。如果你的內容腳本需要存取或修改巢狀框架中的 DOM,請設定
all_frames為true。 這增加了網頁上執行的程式碼量。
只有在需要時才載入內容腳本
為了減少每個網頁載入和執行的程式碼量,並節省記憶體和 CPU 資源,建議只在需要時載入內容腳本,而不是每次載入頁面都載入。
設定何時載入內容腳本到你的擴充清單檔案
要控制擴充功能內容腳本何時載入,請使用 run_at 擴充功能清單檔案中的屬性。
預設情況下,這個屬性會設定為 值 document_idle ,表示內容腳本會在頁面載入完成且 DOM 準備好後才被載入並執行。 這是大多數內容腳本的建議值。 此 document_idle 值確保內容腳本不會干擾頁面載入過程。
要在頁面尚未完全載入前載入並執行內容腳本,請使用 document_start or document_end 值。 這些值在修改網頁版面或樣式等情況下很有用,但也可能造成效能問題或與其他腳本相容性問題。
在執行時以程式化載入內容腳本
要在執行時程式化載入內容腳本,只有在需要時,請使用 chrome.scripting API 。 API chrome.scripting 提供更多控制,讓你的內容腳本何時、在哪裡載入。
例如,你可以在使用者與網頁或擴充功能介面互動後,例如點擊擴充功能的按鈕或網頁部分,才使用 chrome.scripting API 載入內容腳本。
如果你在使用者與網頁互動時使用 chrome.scripting API,務必仔細考慮每次互動時是否都需要反覆載入內容腳本。 過頻繁載入內容腳本可能導致使用者體驗問題或錯誤。
避免阻擋通話或長時間執行的同步任務
阻擋呼叫和長時間同步任務可能會延遲網頁載入或使網頁其他部分變慢,並負面影響使用者介面的響應速度。
阻擋呼叫是 JavaScript 操作,阻止執行其他程式碼直到完成。 例如,使用 XMLHttpRequest同步的 、 localStorage或 chrome.storage.sync API (,) 會防止網頁執行其他程式碼。
長時間執行的同步任務 是指需要較長時間才能完成的同步任務,導致瀏覽器在執行時無法執行其他網頁程式碼。 這可能包括複雜的計算、迴圈或字串操作。
盡可能使用非同步或非阻塞程式碼,例如 Fetch API、JavaScript Promises 或 Web Workers。 非同步或非阻塞程式碼允許在等待任務完成時執行其他程式碼,且不會阻擋執行網頁的瀏覽器程序。
雖然使用 Web Workers 將複雜的程式碼邏輯移到另一個執行緒是個好做法,但對於 CPU 核心數低或已經很忙的裝置,仍可能拖慢速度。
以下是一個使用 Fetch API 的範例。 資料擷取過程中,瀏覽器不會被阻擋,且可執行其他程式碼:
// Asynchronously load data from a JSON file.
fetch("data.json")
.then(response => response.json())
.then(data => {
// Do something with the data.
});
非同步儲存資料
要在擴充功能中儲存資料,請使用 chrome.storage.local API 而非 localStorage API,API 是同步式 API。 API chrome.storage.local 是非同步的,能更有效率地儲存和檢索資料,且不會影響你執行擴充功能的網頁效能。 例如,你可以用這個 chrome.storage.local.get 方法取得先前儲存的值,然後用回調函式使用結果:
chrome.storage.local.get("key", result => {
// Do something with the result.
});
非同步傳送訊息
要在內容腳本與擴充功能背景頁面或其他內容腳本之間通訊,請使用 chrome.runtime.sendMessage OR chrome.tabs.sendMessage 方法。 這些方法是非同步且非阻塞的,允許你在擴充功能的不同部分之間傳送和接收訊息。 你可以用承諾或回電來處理訊息的回應。 例如,你可以用這個 chrome.runtime.sendMessage 方法將訊息傳送到背景頁面,然後用回傳 Promise 的物件來處理回應:
chrome.runtime.sendMessage({type: 'request', data: 'some data'})
.then(response => {
// Do something with the response.
});
從主執行緒執行密集任務
使用 Web Workers 在內容腳本中執行密集任務,同時不會阻擋瀏覽器用來渲染網頁的執行緒。 透過使用 Web Workers,執行密集任務的程式碼會運行在獨立執行緒中。 網頁工作者能提升你內容腳本及其運行網頁的效能與回應速度。
建立網頁工作者會建立一個新的執行緒,該執行緒會使用裝置上的資源。 在低階裝置上使用過多資源可能會導致效能問題。
要在內容腳本和 Web Worker 之間溝通,請使用 postMessage API onmessage 和 API。 例如,要建立新的 Web Worker 並發送訊息給它,請使用以下程式碼:
// Create a new Web Worker.
cons worker = new Worker('worker.js');
// Send a message to the Web Worker.
worker.postMessage({type: 'task', data: 'some data'});
要在您的 Web Worker 中接收訊息並回傳訊息:
// Listen to messages that are sent to the Web Worker.
onmessage = event => {
const type = event.data.type;
const data = event.data.data;
// Do something with the type and data.
// ...
// Send a message back.
postMessage({type: 'result', data: 'some result'});
};
另請參閱
Chrome 擴充功能文件:
MDN: