前言
在项目开发的过程中,第一次遇到了双浏览器之间需要通信的需求,通过查阅各种方法发现各位大佬的实现方法对于目前的项目而言,都不是特别合适。
最后通过查阅MDN(window)得到了灵感,通过window.open和window.opener两个API简单实现了浏览器之间的通信。但是目前这个方法只支持同源的浏览器之前通信,如果想要不同源之间的浏览器通信则需要使用其他方法。
建立通信通道
-
新建一个class 用于存储各个屏幕的信息,这里可以根据个人的需求做调整
//利用 window.open 和 window.opener 简单解决双屏通信问题
class ScreenWin {
constructor(name, screenWins, getMesCallBack) {
this.name = name; //当前浏览器名称
// 数据类型使用Object name-ScreenWin的一个实例
this.screenWins = screenWins; //两个浏览器时使用
// this.setScreenWins(screenWins);
//接受消息后的回调函数
this.getMesCallBack = getMesCallBack;
}
// 多个浏览器时使用这个
setScreenWins(screen) {
this.screenWins = screen; //{XXX:ScreenWin}
for (const key in screen) {
if (Object.hasOwnProperty.call(screen, key)) {
let screenWin = screen[key].screenWins; //{XXX:ScreenWin}
for (const key in screenWin) {
if (Object.hasOwnProperty.call(screenWin, key)) {
const element = screenWin[key]; //{XXX:ScreenWin}
if (key != this.name) {
this.screenWins[key] = element || this.screenWins[key];
}
}
}
}
}
}
//接收到更新信息
UpdateScreenWin(name, screenWin) {
this.screenWins[name] = screenWin;
}
//通知各位 我这里面需要更新
sendUpdateScreenWin(name) {
for (const key in this.screenWins) {
if (Object.hasOwnProperty.call(this.screenWins, key)) {
this.screenWins[key]?.UpdateScreenWin(name, this);
}
}
}
//关闭窗口 根据需求可以重写此方法
closeScreen() {
this.senMessages({ key: "close" });
}
//批量发送消息
senMessages(mes) {
for (const key in this.screenWins) {
if (Object.hasOwnProperty.call(this.screenWins, key)) {
this.senMessage(key, mes);
}
}
}
//单个屏幕发送消息
senMessage(name, mes) {
this.screenWins[name] && this.screenWins[name].getMessage(mes);
}
getMessage(mes) {
this.getMesCallBack(mes);
}
}
-
利用window.open打开新的浏览器,同时建立单个通道
-
mainScreen 浏览器打开 otherScreen浏览器的操作
//mainScreen 浏览器打开 otherWin浏览器的操作
let otherScreen = window.open("https://XXXX", "openName");
window.screenWin = new ScreenWin(
"mainScreen", //当前浏览器名称 多个浏览器时候注意名称不可重复
{
otherScreen: otherScreen.screenWin, //打开的新的浏览的ScreenWin的实例
},
handleMesCallBack //接受消息的回调函数
);
-
otherScreen 被打开的操作,需要通知其他浏览器我当前刚刚被打开。
let mainScreen = window.opener;
window.screenWin = new ScreenWin(
"otherScreen", //当前浏览器名称 多个浏览器时候注意名称不可重复
{
mainScreen: mainScreen.screenWin, //打开的新的浏览的ScreenWin的实例
},
handleMesCallBack //接受消息的回调函数
);
//通知其浏览器需要更新自己的ScreenWin实例
window.screenWin.UpdateScreenWin("otherScreen");
这个时候 mainScreen和otherScreen两个浏览器之间就建立好了通道(ScreenWin的实例)。可以通过senMessages、senMessage方法传递信息,通过handleMesCallBack接受信息。
刷新浏览器需要更新通道
-
mainScreen 刷新的时候会丢失window.screenWin
-
如何判断浏览器是刷新还是关闭
-
mainScreen浏览器刷新,通过window.onunload以及window.onload之间的时间差判断浏览器是刷新还是关闭(目前似乎只有这个唯一的办法去判断浏览器的刷新还是关闭,如果有其他比较好的方法欢迎评论)。
-
mainScreen刷新会丢失window.screenWin这个问题需要在mainScreen再次打开的时候利用 window.open再次拿到otherScreen的screenWin,同时重新赋值window.screenWin。
//关闭浏览器的时候 记录otherWindHash打开的地址
window.onunload = () => {
sessionStorage.setItem("otherWindHash", otherScreen?.location?.hash);
};
//打开浏览器的时候 拿到otherWindHash的地址
window.onload = () => {
otherScreen = window.open(sessionStorage.getItem("otherWindHash") || "默认地址", "openName");
window.screenWin = new ScreenWin(
"mainScreen",
{otherScreen: otherScreen.screenWin},
handleMesCallBack //接受消息的回调函数
);
//通知otherWindHash更新新信息
window.screenWin.sendUpdateScreenWin("mainScreen");
};
-
otherScreen刷新
window.screenWin = new ScreenWin(
"otherScreen",
{ mainScreen: window.opener.screenWin },
handleMesSub
);
window.screenWin.sendUpdateScreenWin("otherScreen");
以上就建立了两个及以上的浏览器通信通道handleMesCallBack方法接收到不同信息就可以各自处理了。
其他通信方法
如果是非同源的浏览器之间的相互通信,可以通过WebSocket、postMessage等方法。
同源浏览器之间还可以使用localStorage通信,localStorage通信就是每个浏览器监听localStorage的变化来进行通信。如果是只有很少的通信需求不想麻烦用localStorage也是挺好的。
window.addEventListener("storage", ListenerMethod);
ListenerMethod = (e)=>{
if(e.key=='XXX'){//XXX消息变化之后做出响应}
}
//关闭监听
window.removeEventListener("storage", ListenerMethod);