跨标签通信的几种方式

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

目录

[1. 使用BroadcastChannel](#1. 使用BroadcastChannel)

[2. 使用SharedWorker](#2. 使用SharedWorker)

[3. 使用webSocket](#3. 使用webSocket)


1. 使用BroadcastChannel

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

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

a页面

html 复制代码
<!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页面

html 复制代码
<!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

javascript 复制代码
// 计时器
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

javascript 复制代码
<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

javascript 复制代码
<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

前端:

html 复制代码
<!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

javascript 复制代码
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 development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;

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

或者使用socket.io插件

客户端:Vue3项目

javascript 复制代码
<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

javascript 复制代码
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 development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;
相关推荐
鱼在在2 小时前
DFS 创建分级菜单
前端·javascript·vue·深度优先·dfs
2401_890666133 小时前
(免费送源码)计算机毕业设计原创定制:Java+SSM+JSP+Ajax+MySQLSSM国外鞋服代购平台
java·javascript·ajax·课程设计·idea·sqoop·tornado
I like Code?3 小时前
Vue3+TypeScript搭建最基础的后台管理系统(含tabs设计)
javascript·vue.js·typescript
练习两年半的工程师5 小时前
React的基础知识:Context
前端·javascript·react.js
Layue000006 小时前
学习HTML第三十三天
java·前端·笔记·学习·html
VillanelleS6 小时前
Vue进阶之Vue CLI服务—@vue/cli-service & Vuex
前端·javascript·vue.js
赔罪6 小时前
最大公约数和最小公倍数-多语言
java·c语言·开发语言·javascript·python·算法
火锅娃6 小时前
在 wordpress 中简易目录插件添加滑动条
javascript·css·json·html5
SRC_BLUE_176 小时前
UPLOAD LABS | PASS 01 - 绕过前端 JS 限制
开发语言·前端·javascript