跨域页面(非同源父子页面)中实现传值

跨域页面(非同源父子页面)中实现传值

2 年前


date:2021/05/20

tag:传值、window.postMessage、父子页面


前端浏览器父子页面获取信息可以通过localstorage/cookie等缓存方式存入、取出来获取,但这只适用于同源页面,如果存在跨域的情况(例如页面中嵌入iframe等)读取缓存区域是读不到数据的。这时H5中的postMessage则不受同源策略限制,可以在非同源父子页面中实现传值。

语法

otherWindow.postMessage(message, targetOrigin, [transfer]);
  • otherWindow
  • 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
  • message
  • 将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
  • targetOrigin
  • 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。 如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
  • transfer 可选
  • 是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

我们以vue中页面为例:父页面为parent.vue,子页面为child.vue

父页面读取子页面 数据代码 示例

parent.vue

<template>
  <iframe src="http://localhost:8080/child" id="myIFrame" style=""></iframe>
</template>
<script>
  export default {
    mounted () {
      this.$nextTick(() => {
        //监听消息反馈
        window.addEventListener('message',function(event) {
          if(event.origin !== 'http://localhost:8080' || event.data.type !=="postMessage") return;
          console.log('received response from child:  ',event.data);
        },false);
</script>

child.vue

<script>
  export default {
    created () {
      var data={type: 'postMessage',data: '我来自子页面'};//需要传递的值
      window.parent.postMessage(data, 'http://localhost:8080/parent')
</script>

运行结果

received response from child:   {type: "postMessage", data: "我来自子页面"}

子页面读取父页面 数据代码 示例

parent.vue

<script>
  export default {
    methods: {
      submit () {
        var myframe = document.getElementById('myIFrame');
        var data={type: 'postMessage',data: '我来自父页面'};//需要传递的值
        myframe.contentWindow.postMessage(data,'http://localhost:8080/child')
        // myframe.contentWindow.parent.postMessage(data, '*') // *不生效
</script>

child.vue

<script>
  export default {
    mounted () {
      this.$nextTick(() => {
        //监听消息反馈
        window.addEventListener('message',function(event) {
          if(event.origin !== 'http://localhost:8080' || event.data.type !=="postMessage") return;