-
背景
chrome manifest v3 由于CSP,禁用了热更新,使得webpack + writetoDisk成为了插件开发的必选项之一,这导致了开发过程中需要不断的刷新service-workder+宿主网站,本文分享我的解决方案 -
实现原理
- 启动项目的同时创建socket 服务器,等待contentjs链接
- 监听开发环境产物文件,如果文件发生变化,发送消息给contentjs
- contentjs 收到消息,通知service-worker更新当前tab和service-worker
-
具体实现
- node ./watch.js - 随项目启动,执行当前脚本,启动socket服务器,脚本内容如下
const chokidar = require('chokidar') const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 1717 }); wss.on('connection', (ws) => { const watcher = chokidar.watch(['./dist/development/js/contentScript.js', './dist/development/background.js'], { persistent: true }); // 添加事件监听 watcher .on('add', path => console.log(`文件 ${path} 已被添加`)) .on('change', path => { console.log(`文件 ${path} 已被修改 `) ws.send("reload"); }) .on('unlink', path => console.log(`文件 ${path} 已被删除`)) .on('error', error => console.log(`发生错误: ${error}`)); });- contentjs 入口处执行以下代码
export const AddHotReload = () => { if (isDev()) { const wsManager = new WebSocketManager("ws://localhost:1717"); //页面加载完毕时,开启监听 window.addEventListener('load', () => { wsManager.connect(); }); document.addEventListener("visibilitychange", () => { //如果当前页面不可见,关闭socket链接,可见的时候链接,避免刷新错误,和节能 if (document.hidden) { wsManager.close(); } else { wsManager.connect(); } }); // 设置收到消息时的回调函数 wsManager.onMessage((message: string) => { console.log('处理接收到的消息:', message); if (!document.hidden) { if (chrome.runtime) { chrome.runtime.sendMessage({ type:9999 }) } else { console.log("runtime empty") } } }); } }//开发环境热更新 export default class WebSocketManager { private url: string; private socket: WebSocket | null = null; private reconnectInterval: number; private maxReconnectAttempts: number; private reconnectAttempts: number; private messageCallback: ((message: string) => void) | null = null; constructor(url: string, reconnectInterval: number = 5000, maxReconnectAttempts: number = 10) { this.url = url; this.reconnectInterval = reconnectInterval; this.maxReconnectAttempts = maxReconnectAttempts; this.reconnectAttempts = 0; } // 初始化 WebSocket 连接 public connect(): void { if (!this.socket || this.socket.readyState === WebSocket.CLOSED) { console.log("正在连接 WebSocket..."); this.socket = new WebSocket(this.url); this.socket.addEventListener('open', (event: Event) => { console.log('WebSocket 连接已打开'); this.reconnectAttempts = 0; // 重置重新连接尝试次数 }); this.socket.addEventListener('message', (event: MessageEvent) => { console.log('接收到消息:', event.data); if (this.messageCallback) { this.messageCallback(event.data); // 调用用户定义的回调函数 } }); this.socket.addEventListener('close', (event: CloseEvent) => { console.log('WebSocket 连接已关闭:', event.code, event.reason); this.attemptReconnect(); // 尝试重新连接 }); this.socket.addEventListener('error', (event: Event) => { console.error('WebSocket 出现错误:', event); }); } } // 关闭 WebSocket 连接 public close(): void { if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.socket.close(); console.log("WebSocket 连接已手动关闭"); } } // 设置收到消息时的回调函数 public onMessage(callback: (message: string) => void): void { this.messageCallback = callback; } // 尝试重新连接 private attemptReconnect(): void { if (this.reconnectAttempts < this.maxReconnectAttempts) { this.reconnectAttempts++; console.log(`尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`); setTimeout(() => { this.connect(); }, this.reconnectInterval); } else { console.log('达到最大重新连接次数,停止尝试'); } } }- service-worker 入口处执行以下代码
chrome.runtime.onMessage.addListener((request: any, sender, sendResponse) => { if (request.type == 9999) { chrome.windows.getCurrent(w => { chrome.tabs.query({ active: true, windowId: w.id }, (tabs: any) => { chrome.tabs.reload(tabs[0].id); chrome.runtime.reload() }) }) } })