首頁  >  問答  >  主體

Chrome 擴充功能中的持久 Service Worker

<p>我需要將我的 Service Worker 在 Chrome 擴充功能中定義為持久的,因為我正在使用 webRequest API 來攔截特定請求的表單中傳遞的一些數據,但我不知道如何做到這一點。我已經嘗試了所有方法,但我的 Service Worker 不斷卸載。 </p> <p>如何保持其加載並等待直到請求被攔截? </p>
P粉323224129P粉323224129443 天前634

全部回覆(2)我來回復

  • P粉034571623

    P粉0345716232023-08-25 17:51:37

    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"}]
    });

    回覆
    0
  • P粉386318086

    P粉3863180862023-08-25 00:41:03

    目錄

    • 問題描述

    • 解決方法:

      • 漏洞利用
      離屏 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 事件喚醒軟體

      儘管您可以嘗試訂閱像其他答案中所示的 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 偵聽器以防止標籤意外關閉。

    關於持久性的警告

    您仍然需要保存/恢復狀態(變數),因為不存在持久服務工作人員之類的東西,並且這些解決方法具有如上所述的限制,因此工作人員可以終止。您可以維護儲存中的狀態,範例

    請注意,您不應該只是為了簡化狀態/變數管理而讓您的工作執行緒持久化。這樣做只是為了恢復因重新啟動工作線程而惡化的效能,以防您的狀態重建成本非常昂貴,或者如果您掛接到本答案開頭列出的頻繁事件。

    回覆
    0
  • 取消回覆