跨标签通信的几种方式

以前面试被问到过,就了解了一下。还有其他方式,但是实际开发中,使用第一个就可以了

目录

1. 使用BroadcastChannel

2. 使用SharedWorker

3. 使用webSocket


1. 使用BroadcastChannel

它允许同源(协议、域名、端口都相同)的不同浏览器窗口、标签页、frame 或者 iframe 下的不同文档之间相互通信。

新建一个文件夹后,新建下面两个文件

a页面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><p id="count">0</p><button>count++</button><script>// 在所有标签页中创建一个BroadcastChannel实例const channel = new BroadcastChannel('my-channel')const count = document.getElementById('count')const btn = document.querySelector('button')btn.addEventListener('click', () => {count.innerHTML = parseInt(count.innerHTML) + 1// 发送消息channel.postMessage({ type: 'my-data', payload: parseInt(count.innerHTML) });})</script>
</body></html>

b页面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><p></p><script>// 在所有标签页中创建一个BroadcastChannel实例const channel = new BroadcastChannel('my-channel');var p = document.querySelector('p');// 监听消息channel.addEventListener('message', (event) => {if (event.data.type === 'my-data') {p.innerText = event.data.payload;}})</script>
</body></html>

每次实例化BroadcastChannel构造函数,都会创建一个链接到命名频道的对象。

而命名频道则是给定源(协议、域名、端口)后产生的一个独有频道。只要源(协议、域名、端口)相同的,通过BroadcastChannel实例化后的对象,都可以进行通信

  • postMessage方法向所有监听了相同频道的 BroadcastChannel 对象发送一条消息,消息内容可以是任意类型的数据。
  • message方法当频道收到一条消息时触发。

2. 使用SharedWorker

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker

  • 如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)
  • Worker 则是在后台中创建一个Worker线程,不会影响主线程,Worker线程中的代码执行完成后,会自动塞入到任务队列,等待执行。可以把一些特别耗时,计算量比较大的放到Worker线程中单独执行,依次达到性能优化的作用
  • SharedWorker 顾名思义,会共享线程中的数据

创建的Vue3项目

在public文件夹下新建shared-worker文件夹,然后新建SharedWorker.js

// 计时器
let counter = 0
// 存储所有连接端口的数组
let ports = []// 监听连接
self.addEventListener('connect', e => {const port = e.ports[0]// 把端口对象存起来ports.push(port)// 监听消息port.onmessage = res => {// console.log('共享线程接收到信息:', res.data)switch (res.data) {case 'counter++':counter++break}// console.log('counter:', counter)// 发送消息// port.postMessage(counter)// 向所有端口广播,这样就都能接收到发送的消息,才会触发message事件ports.forEach(p => {p.postMessage(counter)})}
})

HomeView.vue

<template><div><h3>{{ data }}</h3><button @click="worker.port.postMessage('counter++')">点击向共享线程发送消息</button></div>
</template><script setup lang="ts">
import { onUnmounted, ref } from 'vue'const data = ref(0)// 兼容性判断
if (!window.SharedWorker) {throw new Error('当前浏览器不支持SharedWorker')
}// 创建共享线程
const worker = new SharedWorker('/shared-worker/SharedWorker.js')// 启动线程端口
worker.port.start()// 消息处理
const messageHandle = e => {console.log('SharedWorker共享线程counter值:', e.data)data.value = e.data
}// 线程监听消息
worker.port.addEventListener('message', messageHandle)// 记得销毁worker线程
onUnmounted(() => {worker.port.removeEventListener('message', messageHandle)worker.port.close()
})
</script>

AboutView.vue

<template><div><h3>{{ data }}</h3><button @click="worker.port.postMessage('counter++')">点击向共享线程发送消息</button></div>
</template><script setup lang="ts">
import { onUnmounted, ref } from 'vue'const data = ref(0)// 兼容性判断
if (!window.SharedWorker) {throw new Error('当前浏览器不支持SharedWorker')
}// 创建共享线程
const worker = new SharedWorker('/shared-worker/SharedWorker.js')// 启动线程端口
worker.port.start()// 消息处理
const messageHandle = e => {console.log('SharedWorker共享线程counter值:', e.data)data.value = e.data
}// 线程监听消息
worker.port.addEventListener('message', messageHandle)// 记得销毁
onUnmounted(() => {worker.port.removeEventListener('message', messageHandle)worker.port.close()
})
</script>

3. 使用webSocket

前端:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><h1>WebSocket 测试</h1><p>WebSocket 是一种网络通信协议,提供全双工、双向的通信。</p><input type="text" /><button>发送消息</button><script>const ws = new WebSocket("ws://127.0.0.1:3001");const input = document.querySelector("input");const btn = document.querySelector("button");ws.onopen = function () {console.log("连接成功");};ws.onmessage = function (e) {console.log("收到服务端信息:" + e.data);input.value = e.data;};ws.onclose = function () {console.log("连接已关闭");};ws.onerror = function () {console.log("连接出错");};// 向服务端发送消息btn.addEventListener("click", function () {console.log("发送消息:" + input.value);ws.send(input.value);});</script></body>
</html>

后端:使用express创建的node项目

安装node后,全局安装express:npm i -g express

然后创建node项目:express 项目名

创建项目完成后,在项目根目录下安装ws包和nodemon:npm i nodemon ws

修改package.json中的scripts节点:把node改为nodemon运行,这样能实现热更新

修改app.js

var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");var app = express();// 实现webSocket start ----
const WebSocket = require("ws");
// 创建WebSocket服务器,监听端口3001,express服务器的端口是3000,端口要不一样
const server = new WebSocket.Server({ port: 3001 });
server.on("connection", (ws) => {console.log("New client connected");ws.on("message", (message) => {console.log(`Received: ${message}`);// 广播收到的消息给所有客户端server.clients.forEach((client) => {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(message.toString());}});});ws.on("close", () => {console.log("Client disconnected");});
});
// 实现webSocket end ----// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));app.use("/", indexRouter);
app.use("/users", usersRouter);// catch 404 and forward to error handler
app.use(function (req, res, next) {next(createError(404));
});// error handler
app.use(function (err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get("env") === "development" ? err : {};// render the error pageres.status(err.status || 500);res.render("error");
});module.exports = app;

然后运行后端项目即可:npm start

或者使用socket.io插件

客户端:Vue3项目

<template><div><el-input v-model="data" @change="handleInput"></el-input></div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { io } from 'socket.io-client'const data = ref('')const socket = io('http://localhost:3001', {autoConnect: false, // 自动连接// 传递参数query: {name: localStorage.getItem('user'),},// 设置请求头extraHeaders: {token: 'Bearer authorization_token_here',},reconnection: true, // 自动重连reconnectionAttempts: 10, // 重连次数
})// 监听连接状态
socket.on('connect', function () {console.log('客户端链接成功')
})// 监听断开连接
socket.on('disconnect', function () {console.log('客户端断开连接')
})// 连接
socket.connect()// 监听输入框
const handleInput = () => {socket.emit('chatMessage', data.value)
}
// 实时接收后台返回的数据
socket.on('chat message', function (msg) {console.log('接收消息', msg)data.value = msg.data
})
</script>

服务端:app.js

var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
// const cors = require("cors");var app = express();
// app.use(cors());// socket.io 创建web服务,不同端口
const http = require("http");
const server = http.createServer();
const { Server } = require("socket.io");
const io = new Server(server, {cors: {origin: "*", // 配置客户端可跨域地址},
});
io.on("connection", (socket) => {let { name } = socket.handshake.query;console.log("客户端连接成功,参数:: ", name);let { token } = socket.handshake.headers;console.log("客户端连接成功,token: ", token);// 监听客户端请求socket.on("chatMessage", (msg) => {// 广播给所有客户端io.emit("chat message", {status: 200,data: msg,});});// 监听断开事件socket.on("disconnect", (reason) => {console.log("断开链接原因:", reason);});
});
server.listen(3001, () => {console.log("3001服务启动成功!");
});
// socket.io ------------// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));app.use("/", indexRouter);
app.use("/users", usersRouter);// catch 404 and forward to error handler
app.use(function (req, res, next) {next(createError(404));
});// error handler
app.use(function (err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get("env") === "development" ? err : {};// render the error pageres.status(err.status || 500);res.render("error");
});module.exports = app;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479372.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度神经网络模型压缩学习笔记二:离线量化算法和工具、实现原理和细节

文章目录 一、离线量化基础概念1&#xff09;离线量化定义2&#xff09;离线量化优缺点3&#xff09;如何生产一个硬件能跑的量化模型&#xff1f;4&#xff09;离线量化的类型5&#xff09;如何计算scale&#xff0c;zero_point?6&#xff09;离线量化概念7&#xff09;PTQ与…

HTML详解(1)

1.HTML定义 HTML&#xff1a;超文本标记语言。超文本&#xff1a;通过链接可以把多个网页链接到一起标记&#xff1a;标签&#xff0c;带括号的文本后缀&#xff1a;.html 标签语法&#xff1a;<strong>需加粗文字</strong> 成对出现&#xff0c;中间包裹内容&l…

【21-30期】Java技术深度剖析:从分库分表到微服务的核心问题解析

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Java &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 文章题目&#xff1a;Java技术深度剖析&#xff1a;从分库分表到微服务的核心问题解析 摘要&#xff1a; 本…

Jmeter中的配置原件

2&#xff09;配置原件 1--CSV Data Set Config 用途 参数化测试&#xff1a;从CSV文件中读取数据&#xff0c;为每个请求提供不同的参数值。数据驱动测试&#xff1a;使用外部数据文件来驱动测试&#xff0c;使测试更加灵活和可扩展。 配置步骤 准备CSV文件 创建一个CSV文…

Redis持久化、主从及哨兵架构详解

Redis持久化 RDB快照&#xff08;snapshot&#xff09; 在默认情况下&#xff0c;Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。 你可以对Redis进行设置&#xff0c;让它在“N秒内数据集至少有M个改动”这一条件被满足时&#xff0c;自动保存一次数据集。 比…

双向链表、循环链表、栈

双向循环链表 class Node:#显性定义出构造函数def __init__(self,data):self.data data #普通节点的数据域self.next None #保存下一个节点的链接域self.prior None #保存前一个节点饿链接域 class DoubleLinkLoop:def __init__(self, node Node):self.head nodeself.siz…

【青牛科技】D1671 75Ω 带4级低通滤波的单通道视频放大电 路芯片介绍

概 述 &#xff1a; D1671是 一 块 带 4级 低 通 滤 波 的 单 通 道 视 频 放 大 电 路 &#xff0c; 可 在3V或5V的 低 电 压 下 工 作 。 该 电 路 用 在 有 TV影 象 输 出 功 能 的 产 品 上 面&#xff0c;比如 机 顶 盒 &#xff0c;监 控 摄 象 头 &#xff0c;DVD&#…

Linux服务器生成SSH 密钥对与 GitLab 仓库进行交互

目录 生成 SSH 密钥对 将公钥添加到 GitLab 测试 SSH 连接 生成 SSH 密钥对 在执行脚本的机器上打开终端&#xff0c;执行以下命令&#xff08;假设使用默认的 RSA 算法&#xff0c;一路回车使用默认设置即可&#xff0c;也可以根据需要指定其他算法和参数&#xff09;&…

关于SpringBoot集成Kafka

关于Kafka Apache Kafka 是一个分布式流处理平台&#xff0c;广泛用于构建实时数据管道和流应用。它能够处理大量的数据流&#xff0c;具有高吞吐量、可持久化存储、容错性和扩展性等特性。 Kafka一般用作实时数据流处理、消息队列、事件架构驱动等 Kafka的整体架构 ZooKeeper:…

在Unity中实现物体动画的完整流程

在Unity中&#xff0c;动画是游戏开发中不可或缺的一部分。无论是2D还是3D游戏&#xff0c;动画都能为游戏增添生动的视觉效果。本文将详细介绍如何在Unity中为物体添加动画&#xff0c;包括资源的准备、播放组件的添加、动画控制器的创建以及动画片段的制作与调度。 1. 准备动…

自定义协议

1. 问题引入 问题&#xff1a;TCP是面向字节流的&#xff08;TCP不关心发送的数据是消息、文件还是其他任何类型的数据。它简单地将所有数据视为一个字节序列&#xff0c;即字节流。这意味着TCP不会对发送的数据进行任何特定的边界划分&#xff0c;它只是确保数据的顺序和完整…

Spring Boot 3.4.0 发行:革新与突破的里程碑

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

android 11添加切换分屏功能

引言 自Android 7开始官方就支持分屏显示,但没有切换分屏的功能,即交换上下屏幕。直到Android 13开始才支持切换分屏,操作方式是:分屏模式下双击中间分割线就会交换上下屏位置。本文的目的就是在Android 11上实现切换分屏的功能。 下图是Android13切换分屏演示 切换分屏…

PyTorch基础05_模型的保存和加载

目录 一、模型定义组件——重构线性回归 二、模型的加载和保存 2、序列化保存对象和加载 3、保存模型参数 一、模型定义组件——重构线性回归 回顾之前的手动构建线性回归案例&#xff1a; 1.构建数据集&#xff1b;2.加载数据集(数据集转换为迭代器)&#xff1b;3.参数初…

JavaScript核心语法(3)

前两篇文章大概把JavaScript的基础语法讲了一下&#xff0c;这篇文章主要讲讲ES6的核心语法。ES6的核心语法说实话其实有点多&#xff0c;我重点挑一些经常在项目中用到的来讲&#xff0c;其他一些我没怎么见过的就不讲了。 目录 1.变量和常量 变量&#xff08;let 和 var&a…

爬虫开发(5)如何写一个CSDN热门榜爬虫小程序

笔者 綦枫Maple 的其他作品&#xff0c;欢迎点击查阅哦~&#xff1a; &#x1f4da;Jmeter性能测试大全&#xff1a;Jmeter性能测试大全系列教程&#xff01;持续更新中&#xff01; &#x1f4da;UI自动化测试系列&#xff1a; SeleniumJava自动化测试系列教程❤ &#x1f4da…

NIO三大组件

现在互联网环境下&#xff0c;分布式系统大相径庭&#xff0c;而分布式系统的根基在于网络编程&#xff0c;而netty恰恰是java领域的网络编程的王者&#xff0c;如果要致力于并发高性能的服务器程序、高性能的客户端程序&#xff0c;必须掌握netty网络编程。 NIO基础 NIO是从ja…

34 基于单片机的指纹打卡系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52RC&#xff0c;采用两个按键替代指纹&#xff0c;一个按键按下&#xff0c;LCD12864显示比对成功&#xff0c;则 采用ULN2003驱动步进电机转动&#xff0c;表示开门&#xff0c;另一个…

李宏毅机器学习课程知识点摘要(14-18集)

线性回归&#xff0c;逻辑回归&#xff08;线性回归sigmoid&#xff09;&#xff0c;神经网络 linear regression &#xff0c; logistic regression &#xff0c; neutral network 里面的偏导的相量有几百万维&#xff0c;这就是neutral network的不同&#xff0c;他是…

文件上传upload-labs-docker通关

&#xff08;图片加载不出&#xff0c;说明被和谐了&#xff09; 项目一&#xff1a; sqlsec/ggctf-upload - Docker Image | Docker Hub 学习过程中,可以对照源码进行白盒分析. 补充&#xff1a;环境搭建在Linux虚拟机上的同时&#xff0c;以另一台Windows虚拟机进行测试最…