最大程度地减少扩展对页面加载时间的影响

内容脚本是扩展注入网页并在这些网页上下文中运行的 JavaScript 文件。 使用内容脚本,扩展可以通过读取或更改 DOM 来访问和修改呈现的网页。

但是,内容脚本可能会对网页的性能产生明显影响,例如,减慢页面加载时间。 如果内容脚本在加载页面时运行大量代码,则可能会发生这种情况。

本文提供了最佳做法,可帮助你将扩展对用户访问的网页上的性能影响降到最低。

分析扩展内容脚本

若要分析扩展内容脚本的性能,请使用 Microsoft Edge DevTools 或 Edge 跟踪工具,如以下部分所述。

使用 Microsoft Edge DevTools 分析内容脚本

DevTools 提供了一组功能,用于检查、调试和分析网页使用的代码。 DevTools 还可用于分析扩展的代码。

在本部分中,你将了解如何使用 DevTools 中的 性能 工具来分析扩展的内容脚本。 若要详细了解 性能 工具,请参阅 性能工具简介

  1. 若要打开 DevTools,请右键单击网页,然后选择“检查”。 或者,按 Ctrl+Shift+I(Windows、Linux)或 Command+Option+I (macOS)。 DevTools 随即打开。

  2. 在 DevTools 的 “活动栏”上,选择“ 性能 (性能工具”图标) 选项卡。如果该选项卡不可见,请选择“ 更多工具 ” (“更多工具”图标) >性能”。

  3. 若要开始录制性能配置文件,请单击“ 录制 (录制”图标) 按钮。

  4. 重新加载页面以捕获与页面加载时间对应的分析数据,然后在页面完成加载后,单击“ 停止 (停止”图标) 按钮结束录制。 DevTools 显示记录的性能配置文件:

    DevTools 性能工具中显示的记录的性能配置文件

  5. 若要搜索内容脚本导致的性能事件,请在 Windows/Linux 上按 Ctrl+F ,或在 macOS 上按 Command+F 。 “ 查找 ”文本框显示在 “性能 ”工具的底部。

  6. 键入 “评估脚本 ”,然后按 Enter ,直到 性能 工具突出显示内容脚本导致的性能事件。 当“摘要”面板中的“脚本”标签显示内容脚本的名称时,你会知道你找到了正确的性能事件:

    由于扩展的内容脚本在页面加载期间运行,评估脚本性能事件

使用 Edge 跟踪工具分析内容脚本

URL 中 edge://tracing 提供的 Edge 跟踪工具是一种功能强大的工具,可提供扩展性能的详细分析。 在本部分中,你将了解如何使用 Edge 跟踪工具来了解扩展对页面加载时间的影响。 若要详细了解基于 Perfetto 工具的此跟踪工具,请参阅 Perfetto 跟踪文档中的 Perfetto UI。

  1. 若要打开 Edge 跟踪工具,请打开一个新选项卡或窗口,然后转到 edge://tracing。 跟踪 UI 随即打开。

  2. 若要启动新跟踪,请单击工具左上角的“ 记录 ”按钮。 “ 记录新的跟踪 ”对话框随即打开。

  3. 选择“ 手动选择设置” 选项按钮。 此时将显示类别列表。

  4. 若要捕获有关扩展的内容脚本编译和执行的详细信息,请选择以下所有类别:

    • 扩展
    • v8
    • devtools
    • devtools。时间线
  5. 单击“ 记录 ”按钮。 对话框关闭,Edge 跟踪工具开始记录跟踪。

  6. 打开新选项卡并加载扩展影响的网页。 跟踪工具收集有关扩展在网页上的性能影响的数据。

  7. 打开运行 Edge 跟踪工具的选项卡,然后单击“ 停止 ”按钮。 工具中将显示新的跟踪信息。

筛选结果

Edge 跟踪工具记录的跟踪提供有关浏览器以及扩展的大量信息。

若要筛选信息以仅显示与扩展受影响的网页相关的内容,请执行以下操作:

  1. edge://tracing 页面中,按 Shift+Esc 打开 “浏览器任务管理器 ”对话框。

  2. “浏览器任务管理器 ”对话框中,搜索与扩展受影响的网页对应的选项卡,并记下 “进程 ID ”列中的数字。 关闭该对话框。

  3. 在 Edge 跟踪工具的工具栏中,单击“ 进程”,然后选中与记下的进程 ID 对应的复选框。 清除所有其他复选框。

  4. 在 Edge 跟踪工具的右上角,单击搜索字段,键入 ScriptInjection::InjectJS,然后重复按 Enter ,直到底部面板中突出显示与扩展对应的事件。

    底部面板显示事件的开始时间和总持续时间:

    显示脚本注入事件的边缘跟踪工具

查找关键事件

若要继续分析网页上扩展内容脚本的性能影响,请在 ScriptInjection::InjectJS 事件中查找以下关键事件:

  • v8.compile - 显示内容脚本的编译时间。
  • v8.run - 指示已编译脚本的运行时间。

仅添加扩展功能所需的内容脚本代码

扩展的内容脚本在网页的上下文中运行。 为了尽量减少内容脚本在该网页上的影响,请确保仅在内容脚本中添加扩展在网页上下文中运行所需的最小代码量。 审核内容脚本中的代码,并删除内容脚本不需要在 Microsoft Edge 中运行的旧框架、工具、库或其他代码。

可以使用延迟加载和代码拆分技术来最大程度地减少内容脚本中运行的代码量:

  • 延迟加载 是基于用户操作、页面内容或扩展逻辑,仅在需要时加载代码的过程。

  • 代码拆分 是将代码划分为更小的区块或模块的过程,这些区块或模块可以单独加载或按需加载。

如果扩展足够小,则不需要生成工具来拆分代码。 如果扩展较大,并且代码管理更复杂,请使用生成工具将代码拆分为较小的区块。 生成工具可帮助你将代码组织到逻辑单元中,这些单元可以按需加载。 例如,可以使用 Webpack 将代码拆分为 入口点动态导入

  • 每次加载页面时都会加载入口点。

  • 动态导入仅按需加载,例如当用户与网页或扩展的 UI 交互时:

    // 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

配置内容脚本的加载位置时,请使用以下最佳做法:

  • matches 扩展清单文件中的 和 exclude_matches 属性使用最具体的 URL 模式。 例如,如果内容脚本只需要在 example.com 域的网页上运行,请使用 https://example.com/* 而不是 “*://*/*

  • 若要控制内容脚本是仅在顶级框架中运行,还是在与 URL 模式匹配的网页的嵌套框架中运行,请在扩展清单文件中使用 all_frames 属性。 默认情况下,此属性设置为 false,这意味着内容脚本将仅在顶级帧中运行。 如果内容脚本需要访问或修改嵌套帧中的 DOM,请将此属性设置为 true。 请注意,设置为 all_framestrue 会增加在网页上运行的代码量。

仅在需要时加载内容脚本

若要减少加载和在每个网页上运行的代码量,并节省内存和 CPU 资源,请仅在需要时加载内容脚本,而不是在每个页面加载时加载内容脚本。

配置何时在扩展清单文件中加载内容脚本

若要控制何时加载扩展的内容脚本,请使用 run_at 扩展清单文件中的 属性。

默认情况下,此属性设置为 document_idle 值,这意味着在页面完成加载并准备好 DOM 后,将加载并运行内容脚本。 这是大多数内容脚本的建议值。 值 document_idle 可确保内容脚本不会干扰页面加载过程。

若要在页面完全加载之前加载并运行内容脚本,请使用 document_startdocument_end 值。 这些值在修改网页布局或样式设置等情况下很有用,但它们也可能导致性能问题或与页面上其他脚本的兼容性问题。

以编程方式在运行时加载内容脚本

若要仅在需要时以编程方式在运行时加载内容脚本,请使用 chrome.scripting API 。 通过 chrome.scripting API,可以更好地控制加载内容脚本的时间和位置。

例如,只有在用户与网页或扩展 UI 交互(例如单击扩展的按钮或单击网页的一部分时),才能使用 chrome.scripting API 加载内容脚本。

如果在用户与网页交互时使用 chrome.scripting API,请确保仔细考虑是否需要在每次交互发生时重复加载内容脚本。 过于频繁地加载内容脚本可能会导致用户体验问题或错误。

避免阻止调用或长时间运行的同步任务

阻止调用和长时间运行的同步任务可能会延迟网页加载或使网页的其他方面变慢,并会对 UI 响应能力产生负面影响。

阻止调用是阻止运行其他代码的 JavaScript 操作,直到它们完成。 例如,使用 XMLHttpRequestlocalStoragechrome.storage.sync API (同步) 可防止网页运行其他代码。

长时间运行的同步任务 是需要很长时间才能完成的同步任务,导致浏览器在运行时无法运行其他网页代码。 这可以包括复杂的计算、循环或字符串操作。

尽可能使用异步或非阻塞性代码,例如提取 API、JavaScript 承诺或 Web 辅助角色。 异步或非阻塞代码允许在等待任务完成时执行其他代码,而不会阻止运行网页的浏览器进程。

请注意,尽管使用 Web 辅助角色将复杂的代码逻辑移动到另一个线程是一种很好的做法,但它仍可能会降低 CPU 核心计数或已经繁忙的设备的速度。

下面是使用提取 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 chrome.storage.local 是异步的,可以更有效地存储和检索数据,而不会影响运行扩展的网页的性能。 例如,可以使用 chrome.storage.local.get 方法检索以前存储的值,然后在回调函数中使用结果:

chrome.storage.local.get("key", result => {
  // Do something with the result.
});

异步发送消息

若要在内容脚本与扩展的后台页面或其他内容脚本之间进行通信,请使用 chrome.runtime.sendMessagechrome.tabs.sendMessage 方法。 这些方法是异步且非阻塞的,可用于在扩展的不同部分之间发送和接收消息。 可以使用承诺或回调来处理消息的响应。 例如,可以使用 chrome.runtime.sendMessage 方法将消息发送到后台页面,然后使用返回 Promise 的对象来处理响应:

chrome.runtime.sendMessage({type: 'request', data: 'some data'})
  .then(response => {
    // Do something with the response.
  });

从main线程运行密集型任务

使用 Web 辅助角色在内容脚本中运行密集型任务,而不会阻止浏览器用于呈现网页的线程。 通过使用 Web 辅助角色,运行密集任务的代码在单独的线程中运行。 Web 辅助角色可以提高内容脚本及其运行网页的性能和响应能力。

请注意,创建 Web 辅助角色会创建一个新线程,该线程使用设备上的新资源。 在低端设备上使用过多资源可能会导致性能问题。

若要在内容脚本和 Web 辅助角色之间进行通信,请使用 postMessageonmessage API。 例如,若要创建新的 Web 辅助角色并向其发送消息,请使用以下代码:

// 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 辅助角色中接收消息,并发送回消息,请执行以下操作:

// 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: