最近发现一个问题,我在使用 websocket 的时候,在主页面进行了 websocket 连接了之后,再使用 iframe 打开子页面的时候,通常会触发页面刷新,这样就导致 WebSocket 断开,这是因为切换 src 会重新加载 iframe 内容,使得页面环境重置,进而导致 WebSocket 连接丢失。
问题复现
页面
<div style="width: 100%; height: 100vh; position: fixed; display: flex; margin-top: 5px;left: 0;">
<div style="background: #EDEDED; width: 200px; height: 100%; margin: 0; display: inline-block;">
<el-row class="tac">
<el-col :span="12">
<el-menu style="width: 200px;background-color: #EDEDED" :unique-opened="true">
<el-sub-menu index="1">
<template #title>
<span>系统管理</span>
</template>
<el-menu-item style="background-color: #F7F7F7" index="1-1" @click="customerManagement">用户管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<span>即时通讯</span>
</template>
<el-menu-item style="background-color: #F7F7F7" index="2-1" @click="toContactListPage">联系人</el-menu-item>
<el-menu-item style="background-color: #F7F7F7; position: relative; display: flex; align-items: center;" index="2-2" @click="toMessagePage">消息</el-menu-item>
</el-sub-menu>
</el-menu>
</el-col>
</el-row>
</div>
<div style="width: 100%; height: 100vh; margin-left: 5px;">
<iframe style="background-color: white; width: 100%; height: 100vh; border: none;" :src="iframePage"></iframe>
</div>
</div>
导航
let iframePage = ref("/zecSystemManagement/DataScreen");
// 点击用户管理
const customerManagement = async() => {
iframePage.value = "/zecSystemManagement/UserManagementPage";
};
// 点击好友列表
const toContactListPage = async() => {
iframePage.value = "/zecInstantMessaging/ContactListPage";
};
// 点击消息
const toMessagePage = async() => {
iframePage.value = "/zecInstantMessaging/MessagePage";
};
websocket 连接
这个是 websocket.js 页面获取 websocket 实例的一个方法。
export async function getWebSocket() {
console.log(webSocket);
if (webSocket === undefined){
console.log("ddd");
// 连接 websocket 的方法
await connectWebSocket(getAccountBySessionStorage());
}
return webSocket;
}
打印
控制台打印的就是跳转页面后,获取 websocket 实例,调用 getWebSocket 方法。
可以看到这种写法很不好用,每次点击一个导航栏都会导致 websocket 重连,然后我苦心钻研两天半,发现了下面好用一点的写法。
问题解决
页面
上面导航栏的页面不变,下面的 iframe 使用 keep-alive 替代。
在 Vue.js 中,<keep-alive>是一个内置组件,用于缓存组件实例,避免重复渲染和销毁。
<div style="width: 100%; height: 100vh; margin-left: 5px;">
<!-- <iframe id="iframe" style="background-color: white; width: 100%; height: 100vh; border: none;" :src="iframePage"></iframe>-->
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
导航
markRaw:用于标记一个对象,使其永远不会被 Vue 的反应式系统转换。
import ContactListPage from '@/module/zec-instant-messaging/views/ContactListPage';
import MessagePage from '@/module/zec-instant-messaging/views/MessagePage';
import UserManagementPage from '@/module/zec-system-management/views/UserManagementPage';
let iframePage = ref("/zecSystemManagement/DataScreen");
let currentComponent = ref(null);
// 点击用户管理
const customerManagement = async() => {
currentComponent.value = markRaw(UserManagementPage);
// iframePage.value = "/zecSystemManagement/UserManagementPage";
};
// 点击好友列表
const toContactListPage = async() => {
currentComponent.value = markRaw(ContactListPage);
// iframePage.value = "/zecInstantMessaging/ContactListPage";
};
// 点击消息
const toMessagePage = async() => {
currentComponent.value = markRaw(MessagePage);
// iframePage.value = "/zecInstantMessaging/MessagePage";
};
只需要换这两个地方。
展示
这样就解决了 iframe 多页面切换导致资源刷新的问题,websocket 也不会一直重连浪费资源。
如果有什么不妥或者更好的写法,也可以相互学习学习。