Web Worker线程electron问题怎么解决
本文小编为大家详细介绍“Web Worker线程electron问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Web Worker线程electron问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
初始化项目
electron 开发时会遇到一对多的情况,在进行 websocket 通信时,如果接收到服务端多个指令时,而这个指令刚好需要占用线程,这个时候整个界面就会失去响应,那么我们就可以使用线程来解决这个问题.
npm create vite@latest electron-worker
执行完后修改 package.json 如下:
{ "name": "electron-worker", "private": true, "version": "0.0.0", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": {}, "devDependencies": { "@vitejs/plugin-vue": "^3.2.0", "vite": "^3.2.0", "vue": "^3.2.41", "electron": "19.1.4", "electron-builder": "^23.3.3" }}
编写入口文件和 electron 插件
创建 mainEntry.js 作为 electron 的入口文件,启动一个窗口
// class="lazy" data-src/main/mainEntry.jsimport { app, BrowserWindow } from "electron";process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";let mainWindow;app.whenReady().then(() => { let config = { webPreferences: { nodeIntegration: true, webSecurity: false, allowRunningInsecureContent: true, contextIsolation: false, webviewTag: true, spellcheck: false, disableHtmlFullscreenWindowResize: true, }, }; mainWindow = new BrowserWindow(config); mainWindow.webContents.openDevTools({ mode: "undocked" }); mainWindow.loadURL(process.argv[2]);});
编写 vite 插件,在服务器启动后加载 electron 入口文件
// plugins/devPlugin.jsexport const devPlugin = () => { return { name: "dev-plugin", configureServer(server) { require("esbuild").buildSync({ entryPoints: ["./class="lazy" data-src/main/mainEntry.js"], bundle: true, platform: "node", outfile: "./dist/mainEntry.js", external: ["electron"], }); server.httpServer.once("listening", () => { let { spawn } = require("child_process"); let electronProcess = spawn(require("electron").toString(), ["./dist/mainEntry.js", `http://127.0.0.1:${server.config.server.port}/`], { cwd: process.cwd(), stdio: "inherit", }); electronProcess.on("close", () => { server.close(); process.exit(); }); }); }, };};
使用插件
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import { devPlugin } from "./plugins/devPlugin";export default defineConfig({ plugins: [devPlugin(), vue()],})
将 vue 项目文件放入和 main 同级, 结构如下所示
└─class="lazy" data-src ├─main │ mainEntry.js └─renderer │ App.vue │ main.js ├─assets └─components
修改 index.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" rel="external nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + Vue</title> </head> <body> <div id="app"></div> <script type="module" class="lazy" data-src="/class="lazy" data-src/renderer/main.js"></script> </body></html>
现在执行 npm run dev
就可以运行项目了
websocket
websocket 服务
var WebSocketServer = require('ws').Server;var wss = new WebSocketServer({port: 8181});wss.on('connection', function (ws) { console.log('有客户端连接'); ws.send("连接成功") ws.on('message', function (jsonStr) { console.log(jsonStr.toString()); });});
连接 websocket 服务
准备 Socket 对象
export default class Socket { websocket wsUrl constructor(wsUrl) { this.wsUrl = wsUrl } init() { if (this.websocket) return this.websocket const socket = this.websocket = new WebSocket(this.wsUrl) // WebSocket 接收服务端数据 socket.onmessage = (e) => { console.log("接收服务端消息:", e.data) } // WebSocket 断开连接后触发 socket.onclose = (e) => {} // WebSocket 连接成功 socket.onopen = () => { console.log("连接成功") } // WebSocket 连接异常 socket.onerror = (e) => {} }}
连接 Socket
<script setup>import Socket from './socket'const socket = new Socket("ws://localhost:8181")function register() { socket.init()}</script><template> <div> <button @click="register">注册</button> </div></template><style scoped></style>
点击注册后显示如下:
发送心跳
一般为了确保服务一直连接,需要客户端定时给服务发送心跳
export default class Socket { // ... heartbeatCount // 心跳次数 heartbeatTimer // 心跳定时器 heartbeatInterval = 1000 * 20 // 心跳发送频率(2秒一次) // ... sendHeartBeat() { this.heartbeatCount = 0 if (this.heartbeatTimer) clearInterval(this.heartbeatTimer) this.heartbeatTimer = setInterval(() => { this.websocket.send("发送心跳") }, this.heartbeatInterval) }}
App.vue
function sendHeartBeat() { socket.sendHeartBeat()}
<button @click="sendHeartBeat">发送心跳</button>
可以看到我们在服务端日志里看到有持续心跳日志
取消心跳
因为是定时器发送,当服务端掉线后定时器却还在继续发送,现在我们来优化这个
// 断开连接onclose() { console.log("已断开连接") this.websocket = null // 清除心跳定时器 if (this.heartbeatTimer) clearInterval(this.heartbeatTimer)}
在 socket 断开后进行调用
// WebSocket 断开连接后触发socket.onclose = (e) => { this.onclose()}
重新连接
websocket 断开有可能是客户端网络问题,所以我们需要进行尝试重连
export default class Socket { // ... socketOpen // 是否连接 isReconnect = true // 是否可以重新连接 reconnectCountMax = 3 // 最大重新次数 reconnectTimer // 重连定时器 reconnectCurrent = 0 // 重连次数 reconnectInterval // 1000 * 3 // 重连频率(3秒一次) // ... // 断开连接 onclose() { console.log("已断开连接") this.websocket = null // 清除心跳定时器 if (this.heartbeatTimer) clearInterval(this.heartbeatTimer) // 需要重新连接 if (this.isReconnect) { this.reconnectTimer = setTimeout(() => { if (this.reconnectCurrent >= this.reconnectCountMax) { console.log("超过重连次数,重连失败") clearTimeout(this.reconnectTimer) } else { this.reconnectCurrent += 1 this.reconnect() } }, this.reconnectInterval) } } // 重新连接 reconnect() { console.log("重新连接", this.reconnectCurrent) if (this.websocket && this.socketOpen) { this.websocket.close() } this.init() }}
我们每三秒一次进行尝试重新连接,如果重连三次还未连接,那我们认为无法重新连接
其它优化
export enum PostMessageType { ON_OPEN = 'open', // websocket开启 ON_ERROR = 'error', // websocket异常 ON_CLOSE = 'close', // websocket关闭 ON_MESSAGE = 'message', // websocket接收消息 RECONNECT = 'reconnect', // websocket重新连接 HEARTBEAT = 'heartbeat', // websocket发送心跳 OFF = 'off', // websocket主动关闭 REGISTER = 'register', // websocket注册成功}
class Socket { wsUrl: string // 服务地址 websocket: WebSocket | null = null // websocket对象 socketOpen: boolean = false // socket是否开启 heartbeatTimer: any // 心跳定时器 heartbeatCount: number = 0 // 心跳次数 heartbeatInterval: number = 1000 * 20 // 心跳发送频率(2秒一次) isReconnect: boolean = true // 是否可以重新连接 reconnectCountMax: number = 3 // 最大重新次数 reconnectCurrent: number = 0 // 已发起重连次数 reconnectTimer: any // 重连timer reconnectInterval: number = 1000 * 3 // 重连频率(3秒一次) constructor(url: string) { this.wsUrl = url } // socket 初始化 init() { if (this.websocket) return this.websocket const socket = this.websocket = new WebSocket(this.wsUrl) // WebSocket 接收服务端数据 socket.onmessage = (e) => { this.receive(e.data) } // WebSocket 断开连接后触发 socket.onclose = (e) => { this.postMessage(PostMessageType.ON_CLOSE, e) this.onclose() } // WebSocket 连接成功 socket.onopen = () => { this.onopen() } // WebSocket 连接异常 socket.onerror = (e) => { this.postMessage(PostMessageType.ON_ERROR, e) } } // 连接成功后的回调 onopen() { this.socketOpen = true this.isReconnect = true this.reconnectCurrent = 1 this.heartbeatCount = 0 this.postMessage(PostMessageType.ON_OPEN) } postMessage(type: PostMessageType, data?: any) {} onclose() { this.websocket = null this.socketOpen = false // 清除心跳定时器 clearInterval(this.heartbeatTimer) // 需要重新连接 if (this.isReconnect) { this.reconnectTimer = setTimeout(() => { if (this.reconnectCurrent >= this.reconnectCountMax) { clearTimeout(this.reconnectTimer) } else { this.reconnectCurrent += 1 this.reconnect() } }, this.reconnectInterval) } } reconnect() { this.postMessage(PostMessageType.RECONNECT, this.reconnectCurrent) if (this.websocket && this.socketOpen) { this.websocket.close() } this.init() } send(data: any, callback?: () => void) { const ws = this.websocket if (!ws) { this.init() setTimeout(() => { this.send(data, callback) }, 1000) return } switch (ws.readyState) { case ws.OPEN: ws.send(data) if (callback) { callback() } break case ws.CONNECTING: // 未开启,则等待1s后重新调用 setTimeout(() => { this.send(data, callback) }, 1000) break default: this.init() setTimeout(() => { this.send(data, callback) }, 1000) } } receive(data: any) { this.postMessage(PostMessageType.ON_MESSAGE, data) } sendHeartBeat(data: any) { this.heartbeatCount = 0 if (this.heartbeatTimer) clearInterval(this.heartbeatTimer) this.heartbeatTimer = setInterval(() => { this.send(data, () => { this.heartbeatCount += 1 this.postMessage(PostMessageType.HEARTBEAT, { heartBeatData: data, heartbeatCount: this.heartbeatCount }) }) }, this.heartbeatInterval) } close() { this.isReconnect = false this.postMessage(PostMessageType.OFF, "主动断开websocket连接") this.websocket && this.websocket.close() }}
上面是基础的 websocket ,具体使用需要结合业务进行继承使用
export default class SelfSocket extends Socket { registerData: any // 注册数据 heartBeatData: any // 心跳数据 constructor(url: string) { super(url); } initSocket(registerData: any, heartBeatData: any) { this.registerData = registerData this.heartBeatData = heartBeatData super.init() } onopen() { this.register() super.onopen(); } register() { this.send(this.registerData, () => { this.sendHeartBeat(this.heartBeatData) this.postMessage(PostMessageType.REGISTER, this.registerData) }) } send(data: any, callback?: () => void) { // 数据加密 const str = _encrypt(data) super.send(str, callback); } receive(data: any) { this.postMessage(PostMessageType.ON_MESSAGE, _decode(data)) } postMessage(type: PostMessageType, e?: any) {}}
Worker
创建一个 websocketWorker.js
const URL = "ws://localhost:8181"import Socket from "./socket";const ws = new Socket(URL)self.addEventListener('message', (e) => { const { type, data } = e.data switch (type) { case "init": ws.init(); break case "message": ws.send(data) break case "close": ws.close() break default: console.error("发送websocket命令有误") break }})
<script setup>import Worker from './websocketWorker?worker'const worker = new Worker()worker.onmessage = function (e) { console.log(e.data)}function register() { worker.postMessage({ type: 'init' })}function close() { worker.postMessage({ type: 'close' })}</script><template> <div> <button @click="register">注册</button> <button @click="close">关闭服务</button> </div></template>
vite 使用 worker 可以查看 worker选项
如果是 webpack 可以查看 worker-loader
module.exports = { chainWebpack: config => { config.module .rule('worker') .test(/.worker.js$/) .use('worker-loader') .loader('worker-loader') .options({ inline: 'no-fallback', }) .end() config.module.rule('js').exclude.add(/.worker.js$/) }}
这里是我的配置
读到这里,这篇“Web Worker线程electron问题怎么解决”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341