如何按需创建持久性后台页面? (谷歌浏览器扩展程序)

时间:2016-05-04 02:01:48

标签: google-chrome-extension

备用问题标题:如何防止事件页面被卸载?

我想构建一个行为类似于计时器的扩展程序。它应该在激活时倒数秒,但不应该对非活动做任何事情。

chrome.alarms API很有趣,但没有足够的精度或粒度。它每分钟最多只发射一次,并且可能会发射很晚。如果我想要更频繁地执行某些操作,我就无法使用此API。

然后,下一个自然解决方案是使用background page并在其中使用setTimeoutsetInterval。然而,后台页面是持久的,并且即使在空闲时它们也占用资源(例如,存储器)。所以他们并不理想。

最佳解决方案似乎是event page来运行计时器。但是,文档说:

  

加载后,只要活动,事件页面就会一直运行(例如,调用扩展API或发出网络请求)。

     

[...]

     

一旦事件页面空闲了很短的时间(几秒钟),就会调度runtime.onSuspend事件。在强行卸载之前,事件页面还有几秒钟来处理此事件。

     

[...]

     

如果您的扩展程序使用window.setTimeout()window.setInterval(),请切换为使用警报API。如果事件页面关闭,基于DOM的计时器将不会受到尊重。

不幸的是,拥有有效setInterval并不足以将活动页面视为活动状态。事实上,从我的测试来看,一个长达10秒的间隔足以让事件页面保持运行,但是大于10或15秒的任何东西都相距太远而且事件页面将被卸载。我已在my crx-reload-tab project上对此进行了测试。

我相信我想要的是一个中间立场:

  • 我想要一个可以按需加载和卸载的后台页面。 (而不是一直保持加载的那个。)
  • 我想要一个在内存中保持持久的事件页面,只要我说;但否则可以卸载。 (而不是由浏览器自动卸载的那个。)

有可能吗?我该怎么办?

3 个答案:

答案 0 :(得分:2)

无法按需卸载背景页面,Chrome会为您决定事件页面生命周期(onSuspend无法阻止它)。

如果您关心的是计时器,您可以尝试this answer的解决方案,基本上将计时器拆分为更短的计时器,以实现“稀疏”忙等待。这足以保持事件页面的加载,如果您不需要经常这样做,这是一个可行的解决方案。

一般来说,有些东西会加载事件页面:

  

如果您正在使用消息传递,请务必关闭未使用的消息端口。在关闭所有消息端口之前,事件页面不会关闭。

如果您有任何其他上下文可以保持打开Port,例如内容脚本,则可以利用此功能。有关详细信息,请参阅Long-lived connections docs

在实践中,如果您经常或经常需要精确的亚分钟计时器,则事件页面是一个糟糕的解决方案。使用一个资源获得的资源可能不合理。

答案 1 :(得分:1)

我使用这个功能:

function _doNotSleep() {
    if (isActive) {
        setTimeout(() => {
            fetch(chrome.runtime.getURL('manifest.json'));
            _doNotSleep();
        }, 2000);
    }
}

但是这种方法的问题是Devtools网络选项卡被这个http存根污染了。

答案 2 :(得分:1)

正如Xan的回答中所述,我们可以滥用消息传递。如果您想临时阻止事件页面卸载,那么这也没有错。例如,当使用chrome.notifications API或基于setTimeout / setInterval的任何其他活动显示进度表时,可能会超过默认的卸载超时(5-15秒)。

演示

它将在后台页面中创建一个iframe,并且iframe连接到后台页面。除了manifest.json和后台脚本之外,您还需要使用下面指定的代码制作另外两个文件bg-iframe.html和bg-iframe.js。

manifest.json 摘录:

  "background": {
    "scripts": ["bg.js"],
    "persistent": false
  }

bg.js:

function preventUnload() {
  let iframe = document.querySelector('iframe');
  if (!iframe) {
    iframe = document.createElement('iframe');
    document.body.appendChild(iframe).src = 'bg-iframe.html';
  }
}

function allowUnload() {
  let iframe = document.querySelector('iframe');
  if (iframe) iframe.remove();
}

chrome.runtime.onConnect.addListener(() => {});

bg-iframe.html:

<script src="bg-iframe.js"></script>

bg-iframe.js:

chrome.runtime.connect();

bg.js中的用法示例:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message === 'start') doSomething();
});

function doSomething() {
  preventUnload();
  // do something asynchronous that's spread over time
  // like for example consecutive setTimeout or setInterval calls
  let ticks = 20;
  const interval = setInterval(tick, 1000);

  function tick() {
    // do something
    // ................
    if (--ticks <= 0) done();
  }

  function done() {
    clearInterval(interval);
    allowUnload();
  }
}