首页 > 其他分享 >window.postMessage的使用

window.postMessage的使用

时间:2024-06-05 18:00:44浏览次数:17  
标签:postMessage 系统 window 消息 iframe 使用 event

一、用法

window.postMessage是一个用于在不同的窗口或 frame 之间进行安全跨域通信的方法。它允许一个窗口向另一个窗口发送消息,而无需知道对方的具体来源。

这个方法允许在不同的窗口间建立一种通信机制,可以安全地在跨域环境中传递数据。通过postMessage,你可以向其他窗口发送消息,并在接收窗口中监听和处理这些消息。

基本用法如下:

1、在发送窗口中

otherWindow.postMessage(message, targetOrigin);
  • otherWindow :是一个指向其他窗口的引用,可以是 iframe 的 contentWindow,也可以是打开窗口的引用。
  • message :是要发送的消息,可以是字符串、对象等。
  • targetOrigin :是一个字符串,指定接收消息的窗口的源。只有当接收窗口的 origin(协议、主机和端口)与指定的 targetOrigin 相匹配时,消息才会被发送。

2、在接收窗口中

window.addEventListener('message', function(event) {
  // 处理接收到的消息
});

event参数是一个MessageEvent 对象,包含了有关接收到的消息的详细信息

  • data:包含传递的消息数据。这可以是一个字符串、对象或其他类型的数据,取决于发送方发送的内容。
  • origin:消息的来源。这是一个字符串,表示发送消息的文档的源。它包括协议、主机和端口,如 “http://example.com”。
  • lastEventId :表示消息的 ID,对于服务器发送的事件 (Server-Sent Events) 来说特别有用。
  • source :发送消息的窗口、iframe 或 Worker 对象的引用。这允许你在回复消息时知道消息的来源。
  • ports:如果消息是通过 MessageChannel 对象发送的,这是一个包含所有与通道相关的 MessagePort 对象的数组。

这些属性使得你能够对接收到的消息进行有效地处理,并根据需要采取相应的操作。例如,你可以通过 event.data 访问消息内容,通过 event.origin 验证消息来源的合法性,然后基于这些信息执行相应的逻辑。

二、实践

1、场景:有两个独立部署在不同服务器上的系统A和B的vue项目,想把B嵌入A中,B相当于A里面的一个模块,并且两个系统共用一套人员信息,登录了A系统后打开B系统来,不单独打开新浏览器窗口,只是单纯的路由跳转,并且B系统不需要手动登录,直接用A系统的token或者信息调用接口自动登录。
2、实现:iframewindow.postMessage相结合

错误想法:一般登录信息获取到会存在浏览器的sessionStorage中,既然是在同一个浏览器窗口打开来的项目,那么进入B系统的时候也直接从sessionStorage里面获取一下就好了。实际上有一个前提,两个系统是独立部署在不同服务器上的,A系统的登录信息存在A服务的sessionStorage中,在B系统里面是获取不到的。

在这里插入图片描述
在A系统中新建一个路由页面,通过iframe将B系统嵌入进来,获取到A系统sessionStorage中的用户信息,通过window.postMessage发送给B系统。

假设A系统的服务是http://192.168.62.133:8081,B系统的服务是http://192.168.62.133:8080

<template>
  <!-- 动态路由页面 -->
  <el-main>
    <iframe ref="iframeId" id="iframeId" :src="url" frameborder="0" width="100%" :style="{height:calHeight}" scrolling="auto"></iframe>
  </el-main>
</template>
<script>
export default {
  data () {
    return {
      url: 'http://192.168.62.133:8080'
    }
  },
  mounted () {
    const targetOrigin = this.url;
    const userInfo = JSON.parse(sessionStorage.getItem("UserInfo"))
    var iframe = document.getElementById('iframeId');
    this.$refs.iframeId.onload = () => {
      iframe.contentWindow.postMessage({type: 'login', userInfo}, targetOrigin);
    }
  },
  computed: {
    //计算属性 , 设置iframe高度为窗口高度少100px
    calHeight () {
      return (window.innerHeight - 140) + 'px';
    },
  }
}
</script>
<style lang='scss' scoped>
iframe {
  width: 100%;
  overflow-x: hidden;
}
</style>

在B系统中某个合适的页面如登录页的mounted中执行window.addEventListener开启监听

data () {
  return {
    url: 'http://192.168.62.133:8081'
  }
},
mounted () {
  window.addEventListener('message', this.handleMessage, false)
},
methods: {
  handleMessage (event) {
    if (event.origin == this.url) {
	  if (event.data.type == 'login') {
	    const userInfo = event.data.userInfo; // 接收到的用户信息
	    // 逻辑处理,如登录B系统
	    // 及时关闭监听
	    window.removeEventListener('message', this.handleMessage);
	  }
    }
  }
}

在A系统中进行某些操作,B系统中需要联动的,都可以类似上述在A系统中发送消息,到B系统中接收,如果想要A系统收到B接收到消息的确认,B收到后也可以通过event.source.postMessage反馈给A,A同样使用window.addEventListener监听子系统的消息。

handleMessage (event) {
  if (event.origin == this.url) {
    if (event.data.type == 'logout') {
      console.log('离开子系统');
      sessionStorage.removeItem("UserInfo")
    } else if (event.data.type == 'reLogin') {
      console.log('重新登录');
      sessionStorage.removeItem("UserInfo")
      // 通知父系统页面刷新
      event.source.postMessage({ type: 'childResponseReLogin' }, event.origin);
    }
  }
}

window.addEventListener的回调方法中,除了event参数外还想传递其他参数,可以利用闭包来实现

handleChildResponseFactory(extraParam) {
  // 返回一个处理子系统回复消息的函数
  return function handleMessage (event) {
    // 在函数内部可以访问到除了event外的额外参数
    console.log('Extra parameter:', extraParam);
  };
}
// 创建一个具有额外参数的处理函数
let extraParam = 'someValue'; // 额外参数的值
let handleChildResponseWithExtraParam = this.handleChildResponseFactory(extraParam)
// 监听回复消息,并调用带有额外参数的处理函数
window.addEventListener('message', this.handleChildResponseWithExtraParam);

在父系统的其他页面想要获取到子系统的iframe节点可以通过parent.document.getElementById,如果没有打开子系统来,则返回值为null
如A系统点击侧边栏,想要给B系统发消息

clickMenu(info) {
  let iframe = parent.document.getElementById('iframeId');
  iframe && iframe.contentWindow.postMessage({ type: 'logout' }, this.url);
  this.$router.push(info.path)
}

注意:

① 不能在created或者mounted中直接使用postMessage发送消息,因为可能此时目标系统的页面还未完全加载,可能会导致消息发送失败。在目标系统完全加载后再发送,比如在目标系统的load事件中。

② 接收消息的系统中一定要设置消息的来源且要与发送消息的系统的源一致,否则报错如下:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://192.168.62.133:8080') does not match the recipient window's origin ('http://192.168.62.133:8081').

iframe.contentWindow.postMessage是用于向嵌套的子系统(例如 iframe 中加载的文档)发送消息的方法

iframe.contentWindow.originwindow.origin
它们可能会返回相同的值,表明这两个窗口的源是相同的。然而, window.postMessage 是在全局作用域下调用的,浏览器在处理这个方法时会对目标窗口的源进行严格的校验,具体来说,浏览器会将消息的目标源设置为与当前文档关联的源,而不是 window.origin 返回的值,这样做可以确保消息只能发送到受信任的源,从而防止恶意代码窃取数据或进行其他恶意行为。相比之下,当你使用 iframe.contentWindow.postMessage 时,它是针对特定的 iframe 对象调用的,浏览器会自动将消息的目标源设置为 iframe的源,从而避免了源不匹配的错误。

⑤ 通过window.removeEventListener开启的监听要在合适的时机通过window.removeEventListener及时关闭

标签:postMessage,系统,window,消息,iframe,使用,event
From: https://blog.csdn.net/m0_46613429/article/details/139351299

相关文章

  • 【例0808】create daxis using face 使用面创建基准轴
    文章作者:里海来源网站:NX二次开发官方案例专栏简介《createdaxisusingface根据代码内容,翻译“createdaxisusingface”为“使用面创建基准轴”》这是一个NX二次开发官方小例子,下面是代码和解析。相较于混乱、未经验证的代码,官方案例能够确保开发者获得准确的开发......
  • Allegro如何去掉PCB内层未使用的过孔孔环?
    Allegro如何去掉PCB内层未使用的过孔孔环?Allegro铺铜被过孔大量割断,如何处理?在用Allegro进行PCB设计过程中,遇到铺铜铜箔被焊盘大量割断,导致铺铜不完整,这样就会导致铺铜过电流太小,影响板的性能,会导致板工作不稳定,严重的甚至不工作。如下图。解决办法:是去掉内层未使用的过孔......
  • Vue的viewUI框架安装与使用
    1.安装pycharm进入到项目目录C:\Users\Administrator\PycharmProjects\myvue02>npminstallview-design--save 2.引用在项目的src/main.js中加入如下代码【src/main.js】importVuefrom'vue'importAppfrom'./App.vue'importViewUIfrom'view-design&......
  • 合工大毕业论文LaTeX模板使用指南
    本文是在笔者为班级同学编写的指南的基础上修改而来的。前言LaTeX是目前主流的计算机排版系统之一。目前社区维护了一份合工大毕业论文的LaTeX模板(以下简称“模板”):https://github.com/HFUTTUG/HFUT_Thesis考虑到大部分同学对Word都谈不上精通,使用LaTeX排版毕设论文,可以......
  • validate方法进行表单异步校验时,回调函数内部避免使用全局变量
    对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个promise问题代码:save(){console.log(that.pos.indexName)console.log(that.pos.indexCode)......
  • 使用powershell脚本,自动压缩文件夹,实现备份功能
    担心数据丢失?教你3分钟实现自动压缩,备份文件夹。最近在使用obsidian,想要在多台电脑上访问一个同一个仓库,所以我把obsidian-vault设置在了mac-mini的共享文件夹上。但是有一个问题,mac-mini上边的硬盘是我的闲置硬盘,随时都可能挂掉。一、通过脚本自动备份为了避免obsidian......
  • 【手把手教学】最新ChatTTS语音合成项目使用指南AI变声器chatTTS教程来了!5S夺走你的卧
    像这种充满语气充满感情色彩的人声,再搭配一段自拍图,是由最近大火的AI项目chatTTS生成的,ChatTTS是专门为对话场景设计的文本转语音模型,例如LLM助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。在HuggingFace中开源的版本为4万小......
  • 如何在 Linux 中使用 systemd 安排定期任务?
    systemd是一个系统和服务管理器,它是现代Linux系统中重要的组件之一,主要负责系统的启动和管理。systemd取代了传统的SysVinit和Upstart,成为了大多数Linux发行版的默认init系统。它通过引入并行化启动、依赖关系管理和更强大的功能,提升了系统的启动速度和管理能力......
  • 一文搞懂Kafka,在项目里面更加得心应手的使用
    1.kafka关键概念与术语1.1简单的例子说明kafka的使用场景Apachekafka是消息中间件的一种,我发现很多人不知道消息中间件是什么,在开始学习之前,我这边就先简单的解释一下什么是消息中间件,只是粗略的讲解,目前kafka已经可以做更多的事情。举个例子:生产者消费者,生产者生产鸡蛋......
  • 使用C语言实现链式栈
    一、栈的基本概念        栈(Stack)是一种数据结构,它遵循“后进先出”(LIFO,LastInFirstOut)的原则。这意味着最后一个插入栈的元素最先被删除,你可以理解成一堆盘子,每次只能取最上面的盘子,删除的时候也只能删除最上面的盘子。这样是不是更容易理解了呢?栈的基本操作包......