Vue3 跨标签页或跨窗口通信

在 Vue 应用中,跨标签页或跨窗口的通信通常涉及到两个或多个浏览器标签页之间的信息共享。由于每个标签页或窗口都是独立的 JavaScript 执行环境,它们不能直接通过 Vue 或其他 JavaScript 库来直接相互通信。但是,有一些方法可以实现这种跨标签页的通信,主要依靠浏览器提供的 Web API。

以下是一些常用的跨标签页通信方法:

  • 使用 localStorage 和 storage 事件
  • 使用 Broadcast Channel API

LocalStorage storage 事件

LocalStorage 提供了一种简单的跨页面、跨标签页通信机制。不同标签页或窗口可以通过 localStorage 存储信息,而监听 storage 事件的方式可以在其他标签页检测到这些变化。

通过 localStorage 存储数据

const count = ref(0);
function onSend() {
  count.value++;
  let msg = { message: "LocalStorage 消息:" + count.value };
  localStorage.setItem("message", JSON.stringify(msg));
  state.sData.push(msg);
}

监听 storage 事件

onMounted(() => {
  // 监听 LocalStorage 变化消息
  window.addEventListener("storage", function (e) {
    if (e.key === "message") {
      state.aData.push(JSON.parse(e.newValue));
    }
  });
});

在同一页面使用 window.addEventListener("storage", function (e) {}) 是无效的。

使用 LocalStorage 或 SessionStorage 进行跨标签页数据通信的优点是它们是同步的,并且不需要任何网络通信。然而,这些方法不适用于大型或复杂的数据结构,并且它们可能会引起性能问题,尤其是在存储大量数据或频繁更改数据时。

Broadcast Channel API

Broadcast Channel API 是一个用于在不同窗口或标签页之间进行通信的 API,它允许一个页面向另一个页面发送消息,这些页面可以在同一浏览器实例中打开,或者在不同的浏览器实例中打开。

创建广播频道

在发送消息的页面中,首先需要创建一个 Broadcast Channel,可以使用 new BroadcastChannel(channelName) 构造函数。

channelName 是频道的名称,所有使用相同名称的页面都能收到彼此的消息。

// 创建广播
const channelS = new BroadcastChannel("myChannel");
const count = ref(0);

function onSend() {
  count.value++;
  let msg = { message: "BroadCast Channel 消息:" + count.value };
  // 通过广播频道发送消息
  channelS.postMessage(msg);
  state.sData.push(msg);
}

接收消息

在接收消息的页面中,同样需要创建一个同名的广播频道,并通过 addEventListener 监听消息事件。

const channelA = new BroadcastChannel("myChannel");
channelA.addEventListener("message", function (e) {
  state.aData.push(e.data);
});

关闭频道

当不再需要广播频道时,最好在页面关闭或不再需要通信的时候移除监听器,以释放资源。

切记最好在页面关闭或不再需要通信的时候移除监听器,否则重新进入页面时会再次创建一个监听器,上次创建的监听器还存在,这样的话每次进入页面都会创建一个。

onUnmounted(() => {
  channelA.close();
  // 移除事件监听器
  channel.removeEventListener('message', handleReceivedMessage());
});

注意:使用 Broadcast Channel API 的两个页面必须在同一协议(http 或 https)下,否则无法进行通信。此外,同一页面打开多个标签页也可以通过 Broadcast Channel 进行通信。

示例代码

LocalStorage storage 事件和 Broadcast Channel API 示例

BroadCastChannel.vue

<script setup lang="ts">
import { onUnmounted, onMounted, reactive, ref } from "vue";

const state = reactive({
  sData: [], // 发送消息列表
  aData: [], // 接受消息列表
});

// 发送消息
const channelS = new BroadcastChannel("myChannel");
const count = ref(0);

function onSend() {
  count.value++;
  let msg = { message: "BroadCast Channel 消息:" + count.value };
  channelS.postMessage(msg);
  state.sData.push(msg);
}

// 接受消息
const channelA = new BroadcastChannel("myChannel");
channelA.addEventListener("message", function (e) {
  state.aData.push(e.data);
});

onMounted(() => {
  // 监听 LocalStorage 变化消息
  window.addEventListener("storage", function (e) {
    if (e.key === "message") {
      state.aData.push(JSON.parse(e.newValue));
    }
  });
});

onUnmounted(() => {
  channelS.close();
  channelA.close();
});
</script>
<template>
  <div class="container">
    <h1>BroadCast Channel 通信</h1>
    <el-button @click="onSend" type="primary">发送</el-button>
    <div style="padding: 20px">已发送 {{ count }} 次消息。</div>
    <el-row :gutter="20">
      <el-col :span="12" style="border: 1px solid #ccc">
        <div style="padding: 20px; font-weight: 700">发送消息列表</div>
        <el-alert v-for="item in state.sData" :key="item" :title="item.message" type="warning" :closable="false" style="margin: 10px 0"></el-alert>
      </el-col>
      <el-col :span="12" style="border: 1px solid #ccc">
        <div style="padding: 20px; font-weight: 700">接受消息列表</div>
        <el-alert v-for="item in state.aData" :key="item" :title="item.message" type="success" :closable="false" style="margin: 10px 0"></el-alert>
      </el-col>
    </el-row>
  </div>
</template>
<style lang="scss" scoped>
.container {
  text-align: center;
}
</style>

LocalStorage.vue

<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";

const state = reactive({
  sData: [], // 发送消息列表
  aData: [], // 接受消息列表
});

// 发送消息
const count = ref(0);
function onSend() {
  count.value++;
  let msg = { message: "LocalStorage 消息:" + count.value };
  localStorage.setItem("message", JSON.stringify(msg));
  state.sData.push(msg);
}

</script>
<template>
  <div class="container">
    <h1>LocalStorage 通信</h1>
    <el-button @click="onSend" type="primary">发送</el-button>
    <div style="padding: 20px">已发送 {{ count }} 次消息。</div>
    <el-row :gutter="20">
      <el-col :span="12" style="border: 1px solid #ccc">
        <div style="padding: 20px; font-weight: 700">发送消息列表</div>
        <el-alert
          v-for="item in state.sData"
          :key="item"
          :title="item.message"
          type="warning"
          :closable="false"
          style="margin: 10px 0"
        ></el-alert>
      </el-col>
      <el-col :span="12" style="border: 1px solid #ccc">
        <div style="padding: 20px; font-weight: 700">接受消息列表</div>
        <el-alert
          v-for="item in state.aData"
          :key="item"
          :title="item.message"
          type="success"
          :closable="false"
          style="margin: 10px 0"
        ></el-alert>
      </el-col>
    </el-row>
  </div>
</template>
<style lang="scss" scoped>
.container {
  text-align: center;
}
</style>
### Vue2 Element UI 跨浏览器标签页通信的实现方式 在 Vue 2 项目中使用 Element UI 实现跨浏览器标签页通信,可以通过以下几种方式完成。以下是详细的解决方案: #### 1. 使用 `localStorage` `sessionStorage` 进行数据共享 通过 Web Storage API(如 `localStorage` `sessionStorage`),可以在不同浏览器标签页之间共享数据。当一个标签页更新存储时,另一个标签页可以监听 `storage` 事件并作出响应[^4]。 ```javascript // 标签页 A 写入数据 localStorage.setItem(&#39;sharedKey&#39;, JSON.stringify({ message: &#39;Hello from Tab A&#39; })); // 标签页 B 监听存储事件 window.addEventListener(&#39;storage&#39;, (event) => { if (event.key === &#39;sharedKey&#39;) { const data = JSON.parse(event.newValue); console.log(&#39;Received data from another tab:&#39;, data); } }); ``` 这种方法适用于需要持久化存储的场景,但需要注意数据的安全性和格式化处理[^5]。 #### 2. 结合 Vuex 和 Web Storage 实现状态同步 Vuex 是 Vue 官方提供的状态管理库,适用于管理全局共享的状态。通过结合 `localStorage` `sessionStorage`,可以实现跨标签页的状态同步[^1]。 ```javascript // store.js import Vue from &#39;vue&#39;; import Vuex from &#39;vuex&#39;; Vue.use(Vuex); export default new Vuex.Store({ state: { sharedData: null, // 共享的数据 }, mutations: { updateSharedData(state, payload) { state.sharedData = payload; localStorage.setItem(&#39;sharedData&#39;, JSON.stringify(payload)); // 同步到 localStorage }, }, actions: { setSharedData({ commit }, data) { commit(&#39;updateSharedData&#39;, data); }, }, }); // 在应用初始化时从 localStorage 恢复状态 if (localStorage.getItem(&#39;sharedData&#39;)) { store.commit(&#39;updateSharedData&#39;, JSON.parse(localStorage.getItem(&#39;sharedData&#39;))); } // 监听 storage 事件以同步其他标签页的状态 window.addEventListener(&#39;storage&#39;, (event) => { if (event.key === &#39;sharedData&#39;) { store.commit(&#39;updateSharedData&#39;, JSON.parse(event.newValue)); } }); ``` 这种方式结合了 Vuex 的集中式状态管理和 Web Storage 的跨标签页能力,适合复杂的跨标签页通信需求[^2]。 #### 3. 使用广播通道(Broadcast Channel API) Broadcast Channel API 是一种现代浏览器提供的 API,允许同一源的不同窗口标签页之间进行消息传递[^6]。 ```javascript // 创建广播通道 const channel = new BroadcastChannel(&#39;my_channel&#39;); // 发送消息 channel.postMessage({ message: &#39;Hello from Tab A&#39; }); // 接收消息 channel.onmessage = (event) => { console.log(&#39;Received message from another tab:&#39;, event.data); }; ``` 这种方法的优势在于无需依赖 `localStorage` `sessionStorage`,并且可以实时传递复杂的消息对象[^7]。 #### 4. 利用父组件作为中介 如果标签页是通过路由动态加载的子组件,可以通过父组件作为中介来传递数据。父组件可以维护一个共享的状态对象,并将其通过 `props` 传递给子组件。子组件通过 `$emit` 触发事件通知父组件更新状态[^3]。 ```javascript // ParentComponent.vue <template> <div> <router-view :sharedData="sharedData" @update-shared-data="updateSharedData"></router-view> </div> </template> <script> export default { data() { return { sharedData: null, }; }, methods: { updateSharedData(data) { this.sharedData = data; }, }, }; </script> ``` 在子组件中: ```javascript this.$emit(&#39;update-shared-data&#39;, { message: &#39;Hello from Child&#39; }); ``` 这种方式适用于父子组件关系明确的场景[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值