调试和性能分析 ManifestV3 扩展 Service Worker

2024-01-11

我正在学习如何使用清单 v3 构建 chrome 扩展,我想做的是以下内容

在我的扩展background.js(服务工作者)中,我想这样做:

  • 连接到WebSocket以获取数据更新
  • 当 service-worker 唤醒时重新连接到 Websocket
  • 这些任务是从 WebSocket 获取数据更新、更新徽章文本和发送通知。

我需要执行这些任务,同时不依赖于通过弹出窗口或内容脚本打开端口。

我正在使用 Chrome 闹钟来唤醒 Service Worker

听起来很奇怪,每次 Service Worker 醒来时,我都必须重新连接,因为一旦我关闭扩展开发工具,Chrome 就会每隔 15 秒或更短时间关闭 Service Worker(这让我哭了),但这比发送 XHR 更好定期使用 Chrome 警报,这会导致更多请求发送到 API,因此重新连接到 Websocket 似乎问题不大。

我在 chrome 扩展中调试我的服务工作人员(后台脚本)时遇到了超级困难。问题是,当我打开开发工具时,服务工作线程永远不会变得不活动,而我想做的是确定软件何时醒来执行任务,超级奇怪,因为我需要打开开发工具来调试。 。

如何在未打开 devtools 的情况下调试扩展软件?

您/任何读到这篇文章的人对我想用这个扩展做什么以及调试软件的痛苦过程有什么建议/想法吗?

这是我的 background.js 代码


const extension = {
  count: 0,
  disconnected: false,
  port: {} as any,
  ws: null,
};

const fetchData = () => {
  return fetch(
    'https://api.coingecko.com/api/v3/coins/ethereum?localization=incididuntelit&tickers=false&market_data=true&community_data=true&developer_data=true&sparkline=true'
  ).then((res) => res.json());
};

// Chrome Alarms
const setupAlarms = () => {
  console.log('###ALARMS-SETUP');
  chrome.alarms.get('data-fetch', (alarm) => {
    if (!alarm) {
      chrome.alarms.create('data-fetch', { periodInMinutes: 0.1 });
    }
  });
  chrome.alarms.get('client-pinging-server', (alarm) => {
    if (!alarm) {
      chrome.alarms.create('client-pinging-server', { periodInMinutes: 0.1 });
    }
  });

  chrome.alarms.onAlarm.addListener((e) => {
    if (e.name === 'data-fetch') {
      fetchData()
        .then((res) => {
          // console.log('###ETHEREUM:', res.market_data.current_price.cad);
          chrome.action.setBadgeText({ text: `${Math.round(Math.random() * 100)}` });
        })
        .catch((error) => console.error('###ERROR', error));
    }

    if (e.name === 'client-pinging-server') {
      if (!extension.ws || !extension.ws.getInstance()) {
        console.log('\n');
        console.log('###reconnecting...', extension.ws);
        console.log('\n');
        extension.ws = WebSocketClient();
        extension.ws.connect();
      }
      if (extension.ws.getInstance()) {
        console.log('###sending-client-ping');
        extension.ws.getInstance().send(JSON.stringify({ message: 'client ping - keep alive' }));
      }
    }
  });
};

// ON INSTALL
chrome.runtime.onInstalled.addListener(async (e) => {
  const API_URL = 'ws://localhost:8080';
  chrome.storage.local.set({ apiUrl: API_URL, count: 0 });
  console.info('###Extension installed', e);

  chrome.action.setBadgeText({ text: '0' });
  chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
});

// ON SUSPEND
chrome.runtime.onSuspend.addListener(() => {
  console.log('Unloading.');
  chrome.action.setBadgeText({ text: `off` });
});

function WebSocketClient() {
  let instance = null;
  const connect = () => {
    return new Promise((resolve, reject) => {
      const ws = new WebSocket('ws://localhost:8080');

      const onOpen = () => {
        instance = ws;
        console.log('###websocket:connected', instance);
        return resolve(ws);
      };

      const onError = (event) => {
        console.log('###INIT-FAILED', event);
        ws.close(1000, 'closing due to unknown error');
        return reject('failed to connect to websocket');
      };

      const onClose = () => {
        console.log('###websocket:disconnected');
        instance = null;
        // reconnect is happening in the alarm callback
      };

      ws.onopen = onOpen;
      ws.onerror = onError;
      ws.onclose = onClose;
    });
  };

  const getInstance = () => {
    return instance;
  };

  return {
    connect,
    getInstance,
  };
}

self.addEventListener('install', async (event) => {
  console.log('====install', event);
  chrome.action.setBadgeBackgroundColor({ color: '#a6e22e' });
});

self.addEventListener('activate', async (event) => {
  console.log('====activate', event);
  chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
  extension.ws = WebSocketClient();
  extension.ws.connect();
  setupAlarms();
});

self.addEventListener('push', function (event) {
  // Keep the service worker alive until the notification is created.
  event.waitUntil(
    self.registration.showNotification('Testing PUSH API', {
      body: 'coming from push event',
    })
  );
});

由于 Devtools 可以同时附加到多个上下文,因此您可以为另一个上下文打开它,这样 SW 将成为次要的,从而能够正常卸载。

调试

  1. 打开扩展程序的任何可见页面,或者如果没有,则打开其manifest.json URL:
    chrome-extension://ID/manifest.json where ID是扩展程序的 id
  2. 打开devtools并切换到它Application选项卡,然后选择Service worker在左侧。
  3. Click start(如果显示)要启动服务工作线程,请单击后台脚本名称以在“源”面板中将其打开,设置断点等。
  4. Click stop要停止服务工作线程,可以选择单击Update在顶部,并且skip waiting中间(如果显示)以强制更新。
  5. Click start再次 - 你的断点将触发。

性能分析

  1. 打开扩展程序的任何可见页面,或者如果没有,则打开其manifest.json URL:
    chrome-extension://ID/manifest.json where ID是扩展程序的 id
  2. 打开devtools并切换到它Application选项卡,然后选择Service worker在左侧。
  3. 复制选项卡,打开开发工具,转到性能选项卡,单击“开始”或按 Ctrl-E
  4. 切换回第一个选项卡并单击start按钮(或stop首先,然后start)。在某些情况下,您可能还会看到skip waiting中间,然后单击它。
  5. 切换到第二个选项卡,等待一段时间,再次单击录制按钮或按 Ctrl-E。

Notes

当服务工作线程启动时,您可以在左侧(文件面板中)、右上角(线程面板中)、控制台工具栏(上下文选择器)的“源”面板中看到其上下文。

一开始这可能看起来很笨拙,但是一旦你尝试掌握它的诀窍,它就变得非常简单,甚至可以击败单击 chrome://extensions 页面中的“serviceworker”链接时显示的开发工具,因为这个 a) 显示了扩展的devtools 中的 localStorage/IndexedDB,b) 提供对服务工作线程生命周期/执行的控制,c) 支持软件启动的性能分析。

请注意,ManifestV3 文档中有关服务人员为扩展提供的好处的声明很大程度上被夸大或完全错误,例如在你的情况下,很明显重新启动服务工作者是不好的,所以你应该使用端口来延长软件的使用寿命 https://stackoverflow.com/a/66618269越多越好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调试和性能分析 ManifestV3 扩展 Service Worker 的相关文章

随机推荐