Persistenter Service Worker in der Chrome-Erweiterung
P粉323224129
P粉323224129 2023-08-24 18:56:27
0
2
682
<p>Ich muss meinen Service Worker in einer Chrome-Erweiterung als dauerhaft definieren, weil ich die webRequest-API verwende, um einige Daten abzufangen, die im Formular für eine bestimmte Anfrage übergeben werden, aber ich weiß nicht, wie ich das machen soll. Ich habe alles versucht, aber mein Service Worker wird ständig deinstalliert. </p> <p>Wie halte ich das Laden aufrecht und warte, bis die Anfrage abgefangen wird? </p>
P粉323224129
P粉323224129

Antworte allen(2)
P粉034571623

chrome.webRequest API 不同,chrome.webNavigation API 可以完美地工作,因为 chrome.webNavigation API 可以唤醒 Service Worker,现在您可以尝试将 chrome.webRequest API API 放入 chrome.webNavigation 中。

chrome.webNavigation.onBeforeNavigate.addListener(function(){

   chrome.webRequest.onResponseStarted.addListener(function(details){

      //.............
      
      //.............

   },{urls: ["*://domain/*"],types: ["main_frame"]});


},{
    url: [{hostContains:"domain"}]
});
P粉386318086

目录

  • 问题描述

  • 解决方法:

    • 漏洞利用
    离屏 API
    nativeMessaging API
    WebSocket API
    chrome 消息 API
    • 专用选项卡

  • 注意

根据定义,Service Worker (SW) 不能持久,浏览器必须在一定时间后强制终止其所有活动/请求,在 Chrome 中为 5 分钟。不活动计时器(即没有正在进行的此类活动时)甚至更短:30 秒。

Chromium 团队目前认为这种行为很好(团队偶尔放宽了某些方面,例如 Chrome 114 延长了 chrome.runtime每条消息后的端口),但这仅适用于观察不频繁事件的扩展,这些事件每天只运行几次,从而减少运行之间的浏览器内存占用(例如,带有 url 的 webRequest/webNavigation 事件> 过滤很少访问的网站)。可以重新设计这些扩展以维持状态,示例。不幸的是,这样的田园风光在很多情况下是不可持续的。

已知问题

  • 问题 1:Chrome 106 及更早版本不会针对 webRequest 事件唤醒软件。 p>

    尽管您可以尝试订阅像其他答案中所示的 chrome.webNavigation 这样的 API,但它仅对工作线程启动后发生的事件有帮助。

  • 问题 2:工作人员随机停止因事件而醒来

    解决方法可能是调用 chrome.runtime.reload()。

  • 问题 3:Chrome 109 及更早版本无法延长新 chrome 的软件生命周期 已经运行的后台脚本中的 API 事件。这意味着当事件发生在 30 秒不活动超时的最后几毫秒内时,您的代码将无法可靠地异步运行任何内容。这意味着用户会认为您的扩展不可靠。

  • 问题 4:如果扩展程序维持远程连接或状态(变量)需要很长时间才能重建,或者您观察到如下频繁事件,则性能会比 MV2 差:

    • chrome.tabs.onUpdated/onActivated,
    • chrome.webNavigation 如果范围不限于罕见网址,
    • chrome.webRequest 如果范围不限于罕见的网址或类型,
    • chrome.runtime.onMessage/onConnect 用于所有选项卡中内容脚本的消息。

    为新事件启动 SW 本质上就像打开一个新选项卡。创建环境大约需要 50 毫秒,运行整个 SW 脚本可能需要 100 毫秒(甚至 1000 毫秒,具体取决于代码量),从存储读取状态并重建/水合可能需要 1 毫秒(或 1000 毫秒,具体取决于数据的复杂性) 。即使使用几乎空的脚本,也至少需要 50 毫秒,这对于调用事件侦听器来说是相当巨大的开销,而事件侦听器只需要 1 毫秒。

    SW 每天可能会重新启动数百次,因为此类事件是为了响应具有自然间隙的用户操作而生成的,例如单击一个选项卡,然后写入一些内容,在此期间,软件被终止并为新事件再次重新启动,从而消耗 CPU、磁盘、电池,通常会引入扩展反应的频繁可察觉的滞后。

通过错误利用“持久”服务工作者

Chrome 110 引入了一个错误:调用任何异步 chrome API 都会使工作线程多运行 30 秒。该错误尚未修复。

//背景.js

const keepAlive = () => setInterval(chrome.runtime.getPlatformInfo, 20e3);
chrome.runtime.onStartup.addListener(keepAlive);
keepAlive();

具有离屏 API 的“持久”服务工作人员

凯文·奥古斯托提供。

在 Chrome 109 及更高版本中,您可以使用offscreen API 创建离屏文档并每 30 秒或更短时间从其中发送一些消息,以保持 Service Worker 运行。目前该文档的生命周期不受限制(仅音频播放受到限制,我们不使用),但将来可能会发生变化。

  • manifest.json

      "permissions": ["offscreen"]
    
  • offscreen.html

    <script src="offscreen.js"></script>
    
  • offscreen.js

    setInterval(async () => {
      (await navigator.serviceWorker.ready).active.postMessage('keepAlive');
    }, 20e3);
    
  • 背景.js

    async function createOffscreen() {
      await chrome.offscreen.createDocument({
        url: 'offscreen.html',
        reasons: ['BLOBS'],
        justification: 'keep service worker running',
      }).catch(() => {});
    }
    chrome.runtime.onStartup.addListener(createOffscreen);
    self.onmessage = e => {}; // keepAlive
    createOffscreen();
    

连接 nativeMessaging 主机时“持久”服务工作线程

在 Chrome 105 及更高版本中,只要通过 chrome.runtime.connectNative。如果主机进程由于崩溃或用户操作而终止,则端口将关闭,并且软件将照常终止。您可以通过监听端口的 onDisconnect 事件来防范它再次调用 chrome.runtime.connectNative。

WebSocket 处于活动状态时“持久”服务工作线程

Chrome 116 及更高版本:每隔 30 秒交换一次 WebSocket 消息以保持其活动状态,例如每 25 秒一次。

当可连接选项卡存在时“持久”服务工作者

缺点:

  • 需要一个打开的网页选项卡
  • 内容脚本的广泛主机权限(例如 *://*/*),这会将大多数扩展程序放入网上商店的缓慢审核队列中.

警告!如果您已连接端口,请不要使用此解决方法,请对下面的端口使用另一种解决方法。

警告!如果您使用 sendMessage,还可以实现 sendMessage 的解决方法(如下)。

  • manifest.json,相关部分:

      "permissions": ["scripting"],
      "host_permissions": ["<all_urls>"],
      "background": {"service_worker": "bg.js"}
    
    
  • 后台服务工作者 bg.js:

    const onUpdate = (tabId, info, tab) => /^https?:/.test(info.url) && findTab([tab]);
    findTab();
    chrome.runtime.onConnect.addListener(port => {
      if (port.name === 'keepAlive') {
        setTimeout(() => port.disconnect(), 250e3);
        port.onDisconnect.addListener(() => findTab());
      }
    });
    async function findTab(tabs) {
      if (chrome.runtime.lastError) { /* tab was closed before setTimeout ran */ }
      for (const {id: tabId} of tabs || await chrome.tabs.query({url: '*://*/*'})) {
        try {
          await chrome.scripting.executeScript({target: {tabId}, func: connect});
          chrome.tabs.onUpdated.removeListener(onUpdate);
          return;
        } catch (e) {}
      }
      chrome.tabs.onUpdated.addListener(onUpdate);
    }
    function connect() {
      chrome.runtime.connect({name: 'keepAlive'})
        .onDisconnect.addListener(connect);
    }
    
  • 所有其他扩展页面,例如弹出窗口或选项:

    ;(function connect() {
      chrome.runtime.connect({name: 'keepAlive'})
        .onDisconnect.addListener(connect);
    })();
    

如果你也使用sendMessage

在 Chrome 99-101 中,即使不需要响应,您也需要始终在 chrome.runtime.onMessage 侦听器中调用 sendResponse()。这是MV3 中的一个错误。另外,请确保您在 5 分钟内完成此操作,否则立即调用 sendResponse 并在工作完成后通过 chrome.tabs.sendMessage(到选项卡)或 chrome.runtime.sendMessage(到弹出窗口)发送新消息完成。

如果您已经使用端口,例如chrome.runtime.connect

警告!如果您还将更多端口连接到 Service Worker,则需要在 5 分钟过去之前重新连接每个端口,例如295 秒内。这在 104 之前的 Chrome 版本中至关重要,无论有多少额外的连接端口,它都会杀死 SW。在 Chrome 104 及更高版本中,此错误已修复,但您仍然需要重新连接它们,因为它们的 5 分钟生命周期没有改变,因此最简单的解决方案是在所有版本的 Chrome 中以相同的方式重新连接:例如每 295 秒一次。

  • 后台脚本示例:

    chrome.runtime.onConnect.addListener(port => {
      if (port.name !== 'foo') return;
      port.onMessage.addListener(onMessage);
      port.onDisconnect.addListener(deleteTimer);
      port._timer = setTimeout(forceReconnect, 250e3, port);
    });
    function onMessage(msg, port) {
      console.log('received', msg, 'from', port.sender);
    }
    function forceReconnect(port) {
      deleteTimer(port);
      port.disconnect();
    }
    function deleteTimer(port) {
      if (port._timer) {
        clearTimeout(port._timer);
        delete port._timer;
      }
    }
  • 客户端脚本示例,例如内容脚本:

    let port;
    function connect() {
      port = chrome.runtime.connect({name: 'foo'});
      port.onDisconnect.addListener(connect);
      port.onMessage.addListener(msg => {
        console.log('received', msg, 'from bg');
      });
    }
    connect();
    

“永远”,通过专用选项卡,当选项卡打开时

不使用软件,而是打开一个内部包含扩展页面的新选项卡,因此该页面将充当“可见背景页面”,即软件要做的唯一事情就是打开此选项卡。您也可以从操作弹出窗口中打开它。

chrome.tabs.create({url: 'bg.html'})

它将具有与 ManifestV2 的持久后台页面相同的功能,但 a) 它是可见的,b) 无法通过 chrome.extension.getBackgroundPage 访问(可以替换为 chrome.extension.getViews)。

缺点:

  • 消耗更多内存,
  • 浪费标签栏中的空间,
  • 分散用户的注意力,
  • 当多个扩展程序打开这样的标签时,缺点就会像滚雪球一样越滚越大,并成为真正的 PITA。

您可以通过向页面添加 info/logs/charts/dashboard 来让用户更容易忍受,还可以添加一个 beforeunload 侦听器以防止选项卡被意外关闭。 p>

关于持久性的警告

您仍然需要保存/恢复状态(变量),因为不存在持久服务工作人员之类的东西,并且这些解决方法具有如上所述的限制,因此工作人员可以终止。您可以维护存储中的状态,示例

请注意,您不应该仅仅为了简化状态/变量管理而让您的工作线程持久化。这样做只是为了恢复因重新启动工作线程而恶化的性能,以防您的状态重建成本非常昂贵,或者如果您挂接到本答案开头列出的频繁事件。

Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage