浏览器跨窗口通信

浏览器跨窗口通信

前言

前期主要开发 PC 端网站管理系统,现在在基于前者的基础上需要开发数据统计相关的数据大屏展示。结合现有项目和后续的扩展,综合考虑后决定新开项目主做数据相关的数据大屏展示,以便于后续更新和维护。第一期工作主要是数据展示,基于 PC 端网站携带参数跳转过来。难点是数据大屏的适配,这个网上有很多案例,可以结合自身实际情况,选择适合自己的方案,此处推荐一个数据大屏适配插件「autofit.js」做为参考。

因为是基于 PC 端网站跳转过来的网站链接进行打开预览的,当 PC 端 登录超时 或者 Token 失效 时通过链接跳转的页面无法及时收到通知,还需等待调用 API 时才能知晓。查阅文档后,发现有几种方式是符合我现在项目的使用场景的,现将功能实现做个笔记,便于后续查阅。

方案一

借助 localStorage 实现同源窗口通信;当存储区域(localStoragesessionStorage )被修改时,会触发"storage"事件,通过监听"storage"事件来判断状态的改动,依据获取到的存储区域数据状态改变的数值来对当前页面窗口进行相应的逻辑处理。

javascript 复制代码
// 设置值:value 仅支持 string 类型
localStorage.setItem("key", "value");

// 获取值:返回对应的值,获取不到返回 null
localStorage.getItem("key");

// 移除值
localStorage.removeItem("key");

// 清除值
localStorage.clear();

// 设置监听
window.addEventListener("storage", function (evt) {
	console.log("evt", evt);
});

// 移除监听
window.removeEventListener("storage", function () {
	console.log("evt", evt);
});
js 复制代码
// 设置值:value 仅支持 string 类型
sessionStorage.setItem("key", "value");

// 获取值:返回对应的值,获取不到返回 null
sessionStorage.getItem("key");

// 移除值
sessionStorage.removeItem("key");

// 清除值
sessionStorage.clear();

// 设置监听
window.addEventListener("storage", function (evt) {
	console.log("evt", evt);
});

// 移除监听
window.removeEventListener("storage", function () {
	console.log("evt", evt);
});

同源 的不同文件路径下可以监听到 "storage" 的变化,当前文件存储值发生更新时,当前 页面路径下无法监听到当前文件的变化。

方案二

借助 BroadcastChannel 实现同源窗口页面间的通信;BroadcastChannel 接口代理了一个命名频道,可以让指定 origin 下的任意 browsing context 来订阅;其允许同源的不同浏览器窗口Tab 页面frameiframe 下的不同文档之间互相通信。通过触发监听一个 "message" 事件来接收 BroadcastChannel 发送的消息。通过 BroadcastChannel 实例的 "postMessage" 来发送信息,从而实现多窗口间的消息通知。

jsx 复制代码
// 判断当前浏览器是否支持
if ("BroadcastChannel" in window) {
	// 当前浏览器支持 BroadcastChannel
	console.log(true);
} else {
	// 当前浏览器不支持 BroadcastChannel
	console.log(false);
	return;
}

// 创建实例:相同 origin 才可以通信
const channel = new BroadcastChannel("MSG");

/* 监听方式一 */
channel.onmessage = function (event) {
	// 获取收到的信息
	let msg = event.data;
	console.log("msg", msg);
	if (msg === "xxx") {
		// 吧啦吧啦
	}
};

channel.onmessageerror = function (error) {
	// 监听失败处理
	console.log("error", error);
	// 吧啦吧啦
};

/* 监听方式二 */
channel.addEventListener("message", function (evt) {
	// 获取收到的信息
	let msg = evt.data;
	console.log("msg", msg);
	if (msg === "xxx") {
		// 吧啦吧啦
	}
});

channel.removeEventListener("message", function (evt) {
	// 获取收到的信息
	let msg = evt.data;
	console.log("msg", msg);
	if (msg === "xxx") {
		// 吧啦吧啦
	}
});

注:当前发送页面的窗口,无法监听到发送的信息;除发信息窗口的外的同源窗口可以监听到发送的信息。

Class 实现方式

jsx 复制代码
export default class Channel {
	constructor(typeName) {
		this.typeName = typeName || "MSG";
		this.bcl = null;
	}
	create(cb) {
		if ("BroadcastChannel" in window) {
			this.bcl = new BroadcastChannel(this.typeName);
			this.bcl.addEventListener("message", cb);
		}
	}
	close(cb) {
		if (this.bcl) {
			this.bcl.removeEventListener("message", cb);
			this.bcl.close();
			this.bcl = null;
		}
	}
	sender(msg) {
		if (this.bcl) {
			this.bcl.postMessage(msg || "");
		}
	}
}
jsx 复制代码
import Channel from "./channel.js";

let bcl = null;

// 监听事件
const changeChannel = ({ data }) => {
	if (data === "CLOSE") {
		// todo
		window.close();
		let timer = setTimeout(() => {
			if (!window.closed) location.reload();
			clearTimeout(timer);
		}, 200);
	}
};

// 初始化实例
const initChannel = () => {
	if (bcl) bcl.close(changeChannel);
	bcl = new Channel("LOGIN");
	bcl.create(changeChannel);
};

// 页面卸载
window.onunload = function () {
	if (bcl) bcl.close(changeChannel);
};

其他

本次主要的功能是夸窗口管理当前页的登录状态;主要是前期一个 窗口退出登录 或者 token 失效 时,会 自动刷新 页面 退出登录 跳转 登录页,但是其他页面需要点一下,在 API 请求报错时才能跳转登录页面,感觉这个交互有点繁琐,就在思考能否当前页登出后,其他同源页面也同时退出登录后,跳转登录页或者关闭当前窗口,只留下当前触发激活的窗口的页面。

最终尝试在收到信息的窗口通过触发 window.close() 的方式来关闭当前窗口。于此同时还有一个问题就是,此种方式只能关闭通过 js 打开的窗口,但是非 js 打开的窗口调用 window.close() 方式是不生效的,为了处理这种方式,最终采用如下方式来进行页面的刷新。

js 复制代码
channel.onmessage = function (event) {
	if (event.data === "close") {
		window.close();

		let timer = setTimeout(() => {
			if (!window.closed) {
				window.location.reload();
				clearTimeout(timer);
			}
		}, 100);
	}
};

注:通过判当前窗口是否已关闭的状态来启动延时定时器实现窗口的刷新。

总结

实现窗口之间通信的方式有好多,此处简单罗列的两种方式,其中 "方式二" 是我用在项目中的,已基本满足当前项目的需求,若有更好的方式后续会不断完善和优化。

文章来源:zxkv.github.io/blog/code/3...

相关推荐
小高007几秒前
instanceof 和 typeof 的区别:什么时候该用哪个?
前端·javascript·面试
im_AMBER几秒前
React 03
前端·笔记·学习·react.js·前端框架·react
over6971 分钟前
从代码到歌词:我用AI为汪峰创作了一首情歌
前端
老前端的功夫2 分钟前
JavaScript的`this`指向:送你一张永远不会错的地图
前端
前端没钱2 分钟前
Tauri2+vue3+NaiveUI仿写windows微信,安装包仅为2.5M,95%的API用JavaScript写,太香了
前端·vue.js·rust
用户279428286133 分钟前
HTML5 敲击乐:从零搭建交互式前端音乐项目
前端
KongHen3 分钟前
UTS编写字符串编解码/加密插件(安卓及鸿蒙端)
前端·harmonyos
Cache技术分享11 分钟前
219. Java 函数式编程风格 - 从命令式风格到函数式风格:迭代与数据转换
前端·后端
豆苗学前端13 分钟前
JavaScript原型对象、构造函数、继承与类详解
前端·javascript·后端
飞翔的佩奇16 分钟前
【完整源码+数据集+部署教程】【运动的&足球】足球比赛分析系统源码&数据集全套:改进yolo11-RFAConv
前端·python·yolo·计算机视觉·数据集·yolo11·足球比赛分析系统