欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

html5使用window.postMessage进行跨域实现数据交互的一次实战

程序员文章站 2021-12-07 10:09:19
html5使用window.postMessage进行跨域实现数据交互的一次实战这篇文章主要介绍了html5使用window.postMessage进行跨域实现数据交互的一次实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 21-02-24...

背景

在一个app内嵌的h5中,产品希望在页面的下放设置一个区域,里面展示运营同学通过活动搭建平台生成的教学页面,页面由运营同学自己搭建、替换,产品同学希望h5中能完整展示这个教学页面的内容。

从业务需求上描述,就是一个h5(a页面)内需要通过iframe加载另一个h5页面(b页面)。但是从技术角度来看就有以下几点需要注意的地方:

  • b页面的高度不确定,b页面由活动搭建平台生成,至于展示的内容是什么、有多少并不知道。
  • a页面和b页面是不同源的,因此无法直接通过iframe的contentwindow获取到b页面的尺寸。

html5使用window.postMessage进行跨域实现数据交互的一次实战

解决方法

其实接合1、2点,核心就是需要在a页面获取到b页面的高度,然后调整a页面展示区域的高度,实现在a页面完整展示b页面的功能。

初步想法

这里我想到了使用window.postmessage去解决不同域下页面通讯的问题。a页面不能主动通过不同域的contentwindow获取到b页面的尺寸,那么,让b页面通过window.postmessage通知页面a就好了,自己获取自己的总能获取到。

但是问题来了,b页面不是确定的,是由运营通过搭建平台生成的,也就是说,是没办法在b页面通过代码入侵的方式通知a页面。

进一步想法

实际上,搭建平台中的组件是可实现的。可以通过开发一个“iframe通讯组件”,再基于这个组件搭建一个c页面作为“桥”。那么,因为b页面和c页面都是由搭建平台生成,是同域的,那么,c页面可以主动地通过iframe的contentwindow获取到b页面的高度。最后,a页面和c页面虽然是跨域,但是通过window.postmessage可以实现跨域通讯,a页面只需要监听message事件即可。

有了这样一个作为“桥”的c页面,那么以后不管运营同学通过搭建平台发布什么页面,a页面都是可以完整展示b页面的内容,并且b页面不需要做任何事情。

html5使用window.postMessage进行跨域实现数据交互的一次实战

前置知识

postmessage

otherwindow.postmessage(message, targetorigin, [transfer]);

首先,otherwindow是一个其他窗口的引用,什么情况下可以获得其他窗口呢?可以通过iframe的contentwindow、window.opener(这个页面从哪里打开的)以及window.parent(a页面通过iframe嵌套c页面时,c页面可通过window.parent获取a页面的引用)。
message是一个对象,简单来说,传输时会默认做深拷贝,所以不用担心引用的问题。具体拷贝规则: https://developer.mozilla.org/en-us/docs/web/api/web_workers_api/structured_clone_algorithm (又发现了一个实现深拷贝的方式?)

targetorigin通过这个参数来实现哪些窗口能收到这个消息,如果为'*',那么就是都可以。可以传入一个uri字符串,会通过协议、主机地址、端口去比对,三者中有一者不匹配,就传不过去。
更详细的可以阅读: https://developer.mozilla.org/zh-cn/docs/web/api/window/postmessage

实现

c 页面

先来实现作为“桥”的c页面,这个页面是主要的实现。
首先,c页面是被a页面通过iframe加载的,因此,这里通过获取url上参数的方式,获得b页面的链接。

const src = getquerystring('src');

获取到链接后,c页面通过iframe的方式加载b页面,并且添加到文档中。为了能够获取到b页面的链接,我们需要在b页面onload事件触发后再获取,此时页面上的图片已经加载完毕。

const iframe = document.createelement('iframe');
iframe.addeventlistener('load', () => {
    // 关键步骤
});
iframe.src = src;
iframe.style.visibility = 'hidden';
document.body.appendchild(iframe);

最后实现onload事件中的关键步骤:获取b页面的高度、通过 postmessage 发送高度参数给a页面。
获取b页面的高度:

const doc = iframe.contentdocument;
const iframeheight = math.max(doc.body.clientheight, doc.documentelement.clientheight, doc.body.scrollheight, doc.documentelement.scrollheight);

通过 window.parent 获取a页面的 window 对象的引用,最后通过window.parent.postmessage向a页面发送消息:

if (window.parent) {
  window.parent.postmessage(
    {
      type: 'resize-iframe',
      data: {
        height: iframeheight
      }
    },
    '*'
  );
}

a 页面

a页面做的事情就比较简单了,就是一个iframe加载b页面,另一个iframe加载c页面。通过message事件获取最终高度,调整b页面iframe的高度。

const resizehandler = (e) => {
  const data = e.data;
  if (data.type === 'resize-iframe') {
    const { height } = data.data;
    // 这里设置了最小400高度
    this.height = math.max(400, height);
  }
};
window.addeventlistener('message', resizehandler);

总结

通过window.postmessage的方式进行跨域其实也是第一次去实践,以前总是在一些面试复习资料中了解到,但日常的跨域基本上都是用cors的场景(其实cors都不用前端做什么事情),甚至连jsonp都没有。
业务中能用到不常用的方式解决问题,感觉也是挺好的,起码知识不会停留在字面上~

到此这篇关于html5使用window.postmessage进行跨域实现数据交互的一次实战的文章就介绍到这了,更多相关html5 window.postmessage跨域内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!