BroadcastChannel实现浏览器标签页通信
浏览器标签页通信常用于列表页进入详情页所进行的标签页间通信的情况,实际使用例如博客页面的列表和文章编辑页面、音乐网站的列表和音乐播放详情等。
window.open()实现
使用常用的window.open()API也可以实现,但会存在以下问题:
- 打开新页面时浏览器标签页焦点也会跳转到新页面(如果没有标签页焦点不变的需求可以忽略不计);
- 打开新页面导致新页面全局刷新(多页应用或跳转的新页面内容较少可忽略不计);
BroadcastChannel实现
BroadcastChannel 是 Web API 里用于在同源的不同浏览器上下文(像不同的窗口、标签页、iframe 等)间进行通信的机制,因此可以规避以上两个问题:
实现过程
-
创建两个路由页面
- 列表页
/ - 详情页
/detail
- 列表页
-
列表页
<template> <div class="table-box"> <el-table :data="tableData" style="width: 100%"> <el-table-column prop="username" label="用户名" /> <el-table-column prop="sex" label="性别" /> <el-table-column fixed="right" label="操作" min-width="120"> <template #default="scope"> <el-button link type="primary" size="small" @click="handleClick(scope.row)" > 详情 </el-button> </template> </el-table-column> </el-table> </div> </template> <script setup> import { ref } from "vue"; import { userList } from "@/utils/data"; const handleClick = async (row) => { // channel通信 const channel = new BroadcastChannel("user_detail"); const tabCount = localStorage.getItem("TAB_COUNT"); if (!tabCount || tabCount === "1") { await localStorage.setItem("TAB_COUNT", "2"); await sessionStorage.setItem("TAB_ID", row.id); window.open("/detail", "user_detail"); } else { channel.postMessage(row.id); } }; const tableData = ref(userList); </script> -
详情页
<template> <el-descriptions class="margin-top" title="用户信息表" :column="3" size="large" border > <template #extra> <el-button type="primary" @click="goBack">返回</el-button> </template> <el-descriptions-item> <template #label> <div class="cell-item"> <el-icon :style="iconStyle"> <user /> </el-icon> 用户名 </div> </template> {{ curUser.username }} </el-descriptions-item> <el-descriptions-item> <template #label> <div class="cell-item"> <el-icon :style="iconStyle"> <iphone /> </el-icon> 年龄 </div> </template> {{ curUser.age }} </el-descriptions-item> <el-descriptions-item> <template #label> <div class="cell-item"> <el-icon :style="iconStyle"> <location /> </el-icon> 籍贯 </div> </template> {{ curUser.city }} </el-descriptions-item> <el-descriptions-item> <template #label> <div class="cell-item"> <el-icon :style="iconStyle"> <tickets /> </el-icon> 性别 </div> </template> {{ curUser.sex }} </el-descriptions-item> <el-descriptions-item> <template #label> <div class="cell-item"> <el-icon :style="iconStyle"> <office-building /> </el-icon> 住址 </div> </template> {{ curUser.address }} </el-descriptions-item> </el-descriptions> </template> <script setup> import { ref, onMounted } from "vue"; import { useRoute } from "vue-router"; import { userList } from "@/utils/data"; const route = useRoute(); const initialUser = { date: "", username: "", sex: "", age: 18, city: "", address: "", id: "", }; const curUser = ref({ ...initialUser }); const goBack = async () => { await localStorage.setItem("TAB_COUNT", "1"); await sessionStorage.removeItem("TAB_ID"); window.close(); }; const iconStyle = { fontSize: "20px", color: "#409eff", }; const channel = new BroadcastChannel("user_detail"); channel.addEventListener("message", (event) => { console.log("接收到消息:", event.data); sessionStorage.removeItem("TAB_ID"); const curUserId = event.data; const user = userList.find((user) => user.id === curUserId); if (user) { curUser.value = user; } }); onMounted(() => { const curUserId = sessionStorage.getItem("TAB_ID"); if (curUserId) { const user = userList.find((user) => user.id === curUserId); if (user) { curUser.value = user; } } // 监听页面卸载事件 window.addEventListener("unload", async () => { await localStorage.setItem("TAB_COUNT", "1"); await sessionStorage.removeItem("TAB_ID"); }); }); </script>
实现效果

实现原理
使用BroadcastChannel创建通道进行浏览器标签页通信,利用浏览器本地缓存记录标签页打开数量,如果数量为1(仅有列表页,没有详情页),则使用window.open打开;反之如果数量为2,则使用BroadcastChannel通道发送消息实现页面数据更新;
1319

被折叠的 条评论
为什么被折叠?



