浏览器跨窗口通信

浏览器跨窗口通信

前言

前期主要开发 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...

相关推荐
WeiXiao_Hyy26 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡43 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js