备用问题标题:如何防止事件页面被卸载?
我想构建一个行为类似于计时器的扩展程序。它应该在激活时倒数秒,但不应该对非活动做任何事情。
chrome.alarms
API很有趣,但没有足够的精度或粒度。它每分钟最多只发射一次,并且可能会发射很晚。如果我想要更频繁地执行某些操作,我就无法使用此API。
然后,下一个自然解决方案是使用background page并在其中使用setTimeout
或setInterval
。然而,后台页面是持久的,并且即使在空闲时它们也占用资源(例如,存储器)。所以他们并不理想。
最佳解决方案似乎是event page来运行计时器。但是,文档说:
加载后,只要活动,事件页面就会一直运行(例如,调用扩展API或发出网络请求)。
[...]
一旦事件页面空闲了很短的时间(几秒钟),就会调度runtime.onSuspend事件。在强行卸载之前,事件页面还有几秒钟来处理此事件。
[...]
如果您的扩展程序使用
window.setTimeout()
或window.setInterval()
,请切换为使用警报API。如果事件页面关闭,基于DOM的计时器将不会受到尊重。
不幸的是,拥有有效setInterval
并不足以将活动页面视为活动状态。事实上,从我的测试来看,一个长达10秒的间隔足以让事件页面保持运行,但是大于10或15秒的任何东西都相距太远而且事件页面将被卸载。我已在my crx-reload-tab project上对此进行了测试。
我相信我想要的是一个中间立场:
有可能吗?我该怎么办?
答案 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();
}
}