需求

页面跨域消息传递

A页面中用iframe加载了B页面, 这两个页面不同源,需要双向通讯。

页面跨浏览器窗体消息传递

同一个浏览器同时打开A页面 和 B页面, 这两个页面不同源,也需要双向通讯。

原理

postMessage

postMessage 可以实现不同源的页面在iframe中通讯, 但是不能跨浏览器窗体。

localStorage

localStorage 存储的值发生变化时会在当前窗体window触发 storage 事件。 多个同源的页面是共享localStorage,因此浏览器同时打开多个同源页面,只要localStorage改变了,都会收到 storage 事件。

Bridge 桥

postMessage 可不同源通讯,但是不能跨窗体,localStorage可以跨窗体,但是只限同源页面。 不能满足跨窗体不同源的通讯需求。

为了满足需求,整合postMessagelocalStorage的特性,实现既能跨窗体也可以不同源的消息通讯方案,我们称这个方案叫 Bridge 桥

就是为两个窗体或页面之间搭一座桥,把消息通过桥传递给对方。

实现

组成

例如:有2个相互独立应用 应用A 和 应用B 他们之间不同源的。

每个应用都自己的 页面BridgelocalStorage

消息传递过程

同一个浏览器同时打开 应用A的页面应用B的页面,消息通信过程:

  1. 页面A需要发信息给页面B,首先要知道应用B的桥地址(桥类库的页面url),才能搭桥(在页面A创建一个iframe加载应用B的桥页面)。 搭桥好后,通过postMessage把信息传递给应用B
  2. 应用B收到消息后,把消息加上时间戳,确保了消息数据的唯一性,并写入到自己的 localStorage
  3. 应用B的页面侦听了storage事件,当 localStorage发生了变化,即会收到变化后的内容,即收到了页面A发送过来的数据。
  4. 同理,页面B发送信息给页面A,也需要搭桥,把消息发送给 应用A
  5. 应用A收到消息后,并写入到自己的 localStorage
  6. 应用A的页面侦听了storage事件,当 localStorage发生了变化, 即收到了页面B发送过来的数据。

应用

页面通讯

两个互相独立的页面通讯,可以是iframe嵌套或浏览器新窗口

<template>
  <div>
     <el-button type="primary" @click="openDialog">弹窗打开</el-button>
     <el-button type="primary" @click="openWindow">跨窗口打开</el-button>
     <el-button @click="sendMessage">发送消息</el-button>
       <h3>    收到回复:</h3> 
      <p v-for="(item, index) in replyList" :key="index">{{item}}</p> 
     <el-dialog :visible.sync="visible" 
         :footer="false"
         target="body"
         draggable
         maximizable
         title="弹窗" 
         width="500px" 
         height="400px" 
         src="pages/dialog.html">
    </el-dialog>
  </div>
</template>

<script>
import {fire, on} from '@/utils/bridge'
export default {
  data() {
    return {
      visible: false,
      replyList: []
    }
  },
  methods: {
    openDialog() {
      this.visible = true
    },
    openWindow() {
      window.open('pages/dialog.html')
    },
    sendMessage() {
      const data = {content: `消息内容:${new Date().getTime()}`}
      fire({
       bridge: '/iform/bridge/index.html',
       channel: 'SendDialogMessageChannel',
       data: data
      })
      this.$message.success('发送成功')
    },
    messageHandler(data) {
      this.replyList.push(data)
    }
  },
  created() {
    this.messager = on('ReplyMessageChannel', this.messageHandler)
  },
  beforeDestroy() {
     this.messager &&  this.messager.destroy()
  }
}
</script>

界面服务

界面服务是指,A页面提供了某种服务能力,B页面需要使用A页面的服务得到某些结果,B页面可以对A页面发起服务调用。类似API的调用。

template>
  <div>
     <el-button @click="callService">调用服务</el-button> 
     <h3>结果:</h3> 
     <p v-for="(item, index) in results" :key="index">{{item}}</p> 
  </div>
</template>

<script>
import {service} from '@/utils/bridge'
export default {
  data() {
    return {
      results: []
    }
  },
  methods: {
    callService() {
      const opener = window.open('viwes/provider.html')
      opener.onload = () => {
        service({
         name: 'ServiceName',
         bridge: '/iform/bridge/index.html',
         origin:'/iform/bridge/index.html',
         data: {
           id: new Date().getTime()
         },
         callback: (data)=>{
           this.results.push(data)
         }
        })
      }
    }
  }
}
</script>
作者:hugh  创建时间:2023-12-25 15:26
最后编辑:hugh  更新时间:2024-11-25 19:17