建设网站的风险,做设计在哪个网站接单,福清市建设局网站,从化做网站建设文章目录 前言一、SharedWorker 是什么SharedWorker 是什么SharedWorker 的使用方式SharedWorker 标识与独占 二、Demo使用三、使用SharedWorker实现WebSocket共享 前言 最近有一个需求#xff0c;需要实现用户系统消息时时提醒功能。第一时间就是想用WebSocket进行长连接。但… 文章目录 前言一、SharedWorker 是什么SharedWorker 是什么SharedWorker 的使用方式SharedWorker 标识与独占 二、Demo使用三、使用SharedWorker实现WebSocket共享 前言 最近有一个需求需要实现用户系统消息时时提醒功能。第一时间就是想用WebSocket进行长连接。但是前端项目点击跳转需要打开新的标签页。这个时间就会出现新的标签页打开会把老的WebSocket连接挤掉。然后就想到了去共享一个WebSocket连接。就能实现多个标签页消息共享了。 一、SharedWorker 是什么
SharedWorker 是什么
SharedWorker 是一种特殊类型的 Worker可以被多个浏览上下文访问比如多个 windowsiframes 和 workers但这些浏览上下文必须同源。它们实现于一个不同于普通 worker 的接口具有不同的全局作用域SharedWorkerGlobalScope 但是继承自WorkerGlobalScope
SharedWorker 的使用方式
SharedWorker 线程的创建和使用跟 worker 类似事件和方法也基本一样。 不同点在于主线程与 SharedWorker 线程是通过MessagePort建立起链接数据通讯方法都挂载在SharedWorker.port上。
值得注意的是如果你采用 addEventListener 来接收 message 事件那么在主线程初始化SharedWorker()后还要调用 SharedWorker.port.start() 方法来手动开启端口。
// main.js主线程
const myWorker new SharedWorker(./sharedWorker.js);myWorker.port.start(); // 开启端口myWorker.port.addEventListener(message, msg {console.log(msg.data);
})
但是如果采用 onmessage 方法则默认开启端口不需要再手动调用SharedWorker.port.start()方法
// main.js主线程
const myWorker new SharedWorker(./sharedWorker.js);myWorker.port.onmessage msg {console.log(msg.data);
};
SharedWorker 标识与独占 共享工作者线程标识源自解析后的脚本 URL、工作者线程名称和文档源。可以通过第二参数给SharedWorker 命名 实例化一个共享工作者线程
如果你的服务地址正好就是xxx.com那么这三种解析方式就是同一个线程只会创建一个类似同源策略
另外两个会在其原有线程上增加一个端口port需要我们通过创建一个ports数组存起来方便之后数据分发
- 全部基于同源调用构造函数
- 所有脚本解析为相同的 URL
- 所有线程都有相同的名称
new SharedWorker(./sharedWorker.js);
new SharedWorker(sharedWorker.js);
new SharedWorker(https://xxx.com/sharedWorker.js);
如果当其中URL、工作者线程名称和文档源变更时候都会创建新的线程。
改变url这个好理解改变文档源
demo中我又创建了一个page3.html
和另一个SharedWorker2.js
// 创建
page3与page1中唯一不同的就是引用了SharedWorker2.js
const worker new SharedWorker(./SharedWorker2.js); 改变名字
demo中我又创建了一个page4.html
// 创建
page4和page2中唯一不同的就是给了不同的第2个名字两种写法效果相同只不过对象还能传递其他参数
page2中直接给字符串const worker new SharedWorker(./SharedWorker.js,page2);
page4中给了对象const worker new SharedWorker(./SharedWorker.js,{name:page4}); 二、Demo使用
demo演示 demo条件
需要服务器环境运行。我这边使用的是vs code 插件Live Server这玩意咋用自己百度下可以看一下视频里面的地址是127开头的。chrome浏览器这个不用多说了要提一点的是SharedWorker 文件里面的console和debugger是不会出现page1 和page2的控制台的这个需要去专门看线程的地方查看。chrome浏览器通过chrome://inspect/#workers进入。看图
上代码 SharedWorker.js
// 记个数
let count 0;
// 把每个连接的端口存下来
const ports [];// 连接函数 每次创建都会调用这个函数
onconnect (e) {console.log(这里是共享线程展示位置);// 获取端口const port e.ports[0];// 把丫存起来ports.push(port);// 监听方法port.onmessage (msg) {// 这边的console.log是看不到的 debugger也是看不到的 需要在线程里面看console.log(共享线程接收到信息, msg.data, count);if (msg.data ) {count;}// 循环向所有端口广播ports.forEach((p) {p.postMessage(count);});};
};
page1.html
!DOCTYPE html
htmlheadmeta charsetutf-8 /titleSharedWorker-page1/title/headbodyh1SharedWorker-page1/h1button idbtncount/buttonscriptconst btn document.querySelector(#btn);// 兼容性判断if (!SharedWorker) {throw new Error(当前浏览器不支持SharedWorker);}// 创建const worker new SharedWorker(./SharedWorker.js);// 启动worker.port.start();// 线程监听消息worker.port.onmessage (e) {console.log(page1共享线程计数值, e.data);};btn.addEventListener(click, (_) {worker.port.postMessage();});/script/body
/html
page2.hrml
!DOCTYPE html
htmlheadmeta charsetutf-8 /titleSharedWorker-page2/title/headbodyh1SharedWorker-page2/h1button idbtncount/buttonscriptconst btn document.querySelector(#btn);// 兼容性判断if (!SharedWorker) {throw new Error(当前浏览器不支持SharedWorker);}// 创建const worker new SharedWorker(./SharedWorker.js);// 启动worker.port.start();// 线程监听消息worker.port.onmessage (e) {console.log(page2共享线程计数值, e.data);};btn.addEventListener(click, (_) {worker.port.postMessage();});/script/body
/html 上面的代码基本上就已经算是OK了。 三、使用SharedWorker实现WebSocket共享
SharedWorker.js SharedWorker的js文件是需要让各个浏览器页签引用的。所以将文件放在了public中
// 记个数
let count 0;
// 把每个连接的端口存下来
const ports [];
var state {webSocket: null, // webSocket实例lockReconnect: false, // 重连锁避免多次重连maxReconnect: 6, // 最大重连次数 -1 标识无限重连reconnectTime: 0, // 重连尝试次数heartbeat: {interval: 30 * 1000, // 心跳间隔时间timeout: 10 * 1000, // 响应超时时间pingTimeoutObj: null, // 延时发送心跳的定时器pongTimeoutObj: null, // 接收心跳响应的定时器pingMessage: JSON.stringify({type: ping}), // 心跳请求信息},token:null
}// 连接函数 每次创建都会调用这个函数
onconnect (e) {console.log(这里是共享线程展示位置, e);// 获取端口const port e.ports[0];// 把丫存起来ports.push(port);// 监听方法port.onmessage (msg) {// 这边的console.log是看不到的 debugger也是看不到的 需要在线程里面看console.log(共享线程接收到信息, msg);var data msg.data || {}var conf JSON.parse(data)console.log(解析后的参数, conf)switch (conf.type) {case open:console.log(共享线程状态为Open)if (!state.webSocket) {state.tokenconf.tokeninitWebSocket(conf.host, conf.baseURL, conf.uri, state.token, conf.tenant);}breakcase portClose:console.log(共享线程状态为portClose)// 关闭当前端口new SharedWorker 会默认开启端口if (ports.indexOf(port) -1) {ports.splice(ports.indexOf(port), 1)}breakcase wsClose:// 关闭websocketconsole.log(共享线程状态为WsClose)state.webSocket.close();clearTimeoutObj(state.heartbeat);state.websocket nullstate.tokennullbreakcase close:// 关闭SharedWorker 通过self调用 SharedWorkerGlobalScope 的实例console.log(共享线程状态为close)self.close()breakdefault:break}};
};const initWebSocket (host, baseURL, uri, token, tenant) {// ws地址let wsUri ws://${host}${baseURL}${uri}?access_token${token}TENANT-ID${tenant};// let wsUri ws://${host}${baseURL}${other.adaptationUrl(props.uri)}?access_token${token.value}TENANT-ID${tenant.value};// let wsUri ws://${host}${baseURL}${uri}?access_token${token};// 建立连接state.webSocket new WebSocket(wsUri);// 连接成功state.webSocket.onopen onOpen;// 连接错误state.webSocket.onerror onError;// 接收信息state.webSocket.onmessage onMessage;// 连接关闭state.webSocket.onclose onClose;
};const reconnect () {if (!state.token) {return;}if (state.lockReconnect || (state.maxReconnect ! -1 state.reconnectTime state.maxReconnect)) {return;}state.lockReconnect true;setTimeout(() {state.reconnectTime;// 建立新连接initWebSocket();state.lockReconnect false;}, 5000);
};
/*** 清空定时器*/
const clearTimeoutObj (heartbeat) {heartbeat.pingTimeoutObj clearTimeout(heartbeat.pingTimeoutObj);heartbeat.pongTimeoutObj clearTimeout(heartbeat.pongTimeoutObj);
};
/*** 开启心跳*/
const startHeartbeat () {const webSocket state.webSocket;const heartbeat state.heartbeat;// 清空定时器clearTimeoutObj(heartbeat);// 延时发送下一次心跳heartbeat.pingTimeoutObj setTimeout(() {// 如果连接正常if (webSocket.readyState 1) {//这里发送一个心跳后端收到后返回一个心跳消息webSocket.send(heartbeat.pingMessage);// 心跳发送后如果服务器超时未响应则断开如果响应了会被重置心跳定时器heartbeat.pongTimeoutObj setTimeout(() {webSocket.close();}, heartbeat.timeout);} else {// 否则重连reconnect();}}, heartbeat.interval);
};/*** 连接成功事件*/
const onOpen () {console.log(连接成功)//开启心跳startHeartbeat();state.reconnectTime 0;
};
/*** 连接失败事件* param e*/
const onError () {console.log(连接 失败)//重连reconnect();
};/*** 连接关闭事件* param e*/
const onClose () {//重连reconnect();
};
/*** 接收服务器推送的信息* param msgEvent*/
const onMessage (msgEvent) {//收到服务器信息心跳重置并发送console.log(接到消息, msgEvent)startHeartbeat();// const text JSON.parse(msgEvent.data);ports.forEach((p) {p.postMessage(msgEvent.data);});
};定义一个组件叫WebSocket.vue
代码中有一些token的判断可以无视。 我这里怎么简单怎么来。定义一个组件直接放到app.vue中引用(主打的就是一个方便) 我这里接收到消息后使用mitt.js进行各消息分发
templatediv/div
/template
script setup langts nameglobal-websocket
import { Session } from /utils/storage;
import {computed, onMounted, onUnmounted, ref,watch} from vue;
import {eventBus} from /utils/eventBus
import other from /utils/other;const props defineProps({uri: {type: String,},
});
const isLoginrefany()
const workerref()
const token computed(() {return Session.getToken();
});const tenant computed(() {return Session.getTenant();
});
watch(isLogin,(newValue, oldValue) {if(newValue){initWebSocket();}
})
onMounted(() {// initWebSocket();if(sessionStorage.getItem(token)){initWebSocket();}else{window.addEventListener(setItem, () {isLogin.value sessionStorage.getItem(token)});}
});onUnmounted(() {let conf{type:wsClose,}worker.value.port.postMessage(JSON.stringify(conf))
});const initWebSocket () {if (!SharedWorker) {throw new Error(当前浏览器不支持SharedWorker);}
// 创建worker.value new SharedWorker(../../../public/SharedWorker.js);// 线程监听消息worker.value.port.onmessage (e:any) {console.log(接受到消息, e.data);sendEventBus(JSON.parse(e.data))};let conf{type:open,host:window.location.host,baseURL:import.meta.env.VITE_API_URL,uri:other.adaptationUrl(props.uri),token:token.value,tenant:tenant.value}worker.value.port.postMessage(JSON.stringify(conf))
};
const sendEventBus(text:any){switch (text.type){case pong:return;case discuss:eventBus.emit(discuss, text);break;case onlineusers:eventBus.emit(onlineusers, text);break;case livestart:eventBus.emit(livestart, text);break;case message_notify:eventBus.emit(message_notify, text);break;}
}
/script
mitt消息总线的使用 npm install --save mitt // eventBus.ts
import createEventBus from mitt;export const eventBus createEventBus();
使用
import {eventBus} from /utils/eventBus//发送消息
eventBus.emit(discuss, text);//监听消息
eventBus.on(discuss, (data) {console.log(data)});本文借鉴https://blog.csdn.net/jinke0010/article/details/124248321