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


Posted in HTML / CSS onFebruary 24, 2021

背景

在一个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跨域内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章,希望大家以后多多支持三水点靠木! 

HTML / CSS 相关文章推荐
CSS3画一个阴阳八卦图
Mar 09 HTML / CSS
绝对令人的惊叹的CSS3折叠效果(3D效果)整理
Dec 30 HTML / CSS
深入浅析css3 border-image边框图像详解
Nov 24 HTML / CSS
利用CSS3 动画 绘画 圆形动态时钟
Mar 20 HTML / CSS
HTML5样式控制示例代码
Nov 27 HTML / CSS
html5使用canvas实现跟随光标跳动的火焰效果
Jan 07 HTML / CSS
使用Html5实现异步上传文件,支持跨域,带有上传进度条
Sep 17 HTML / CSS
Canvas实现保存图片到本地的示例代码
Jun 28 HTML / CSS
html5 canvas 实现光线沿不规则路径运动
Apr 20 HTML / CSS
z-index不起作用
Mar 31 HTML / CSS
css3 实现文字闪烁效果的三种方式示例代码
Apr 25 HTML / CSS
css清除浮动clearfix:after的用法详解(附完整代码)
May 21 HTML / CSS
Canvas在超级玛丽游戏中的应用详解
Feb 06 #HTML / CSS
Html5移动端网页端适配(js+rem)
Feb 03 #HTML / CSS
使用HTML和CSS实现的标签云效果(附demo)
Feb 03 #HTML / CSS
canvas版人体时钟的实现示例
Jan 29 #HTML / CSS
h5页面背景图很长要有滚动条滑动效果的实现
Jan 27 #HTML / CSS
ivx平台开发之不用代码实现一个九宫格抽奖功能
Jan 27 #HTML / CSS
详解如何将 Canvas 绘制过程转为视频
Jan 25 #HTML / CSS
You might like
PHP个人网站架设连环讲(一)
2006/10/09 PHP
PHP基础学习之流程控制的实现分析
2013/04/28 PHP
PHP+Ajax验证码验证用户登录
2016/07/20 PHP
Yii框架学习笔记之session与cookie简单操作示例
2019/04/30 PHP
javascript SpiderMonkey中的函数序列化如何进行
2012/12/05 Javascript
jQuery 事件的命名空间简单了解
2013/11/22 Javascript
js/jquery判断浏览器的方法小结
2014/09/02 Javascript
基于jQuery实现的扇形定时器附源码下载
2015/10/20 Javascript
jQuery原理系列-常用Dom操作详解
2016/06/07 Javascript
将JSON字符串转换成Map对象的方法
2016/11/30 Javascript
Vue.js系列之项目搭建(1)
2017/01/03 Javascript
js实现背景图自适应窗口大小
2017/01/10 Javascript
基于JavaScript实现本地图片预览
2017/02/08 Javascript
Js利用console计算代码运行时间的方法示例
2017/09/24 Javascript
Angular实现模版驱动表单的自定义校验功能(密码确认为例)
2018/05/17 Javascript
基于vue.js实现分页查询功能
2018/12/29 Javascript
Nodejs实现的操作MongoDB数据库功能完整示例
2019/02/02 NodeJs
vue 接口请求地址前缀本地开发和线上开发设置方式
2020/08/13 Javascript
Element-ui 自带的两种远程搜索(模糊查询)用法讲解
2021/01/29 Javascript
Django中的“惰性翻译”方法的相关使用
2015/07/27 Python
Python使用PDFMiner解析PDF代码实例
2017/03/27 Python
Python读csv文件去掉一列后再写入新的文件实例
2017/12/28 Python
修复CentOS7升级Python到3.6版本后yum不能正确使用的解决方法
2018/01/26 Python
python微信公众号开发简单流程
2018/03/23 Python
下载python中Crypto库报错:ModuleNotFoundError: No module named ‘Crypto’的解决
2018/04/23 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
python中查看.db文件中表格的名字及表格中的字段操作
2020/07/07 Python
Html5新增标签与样式及让元素水平垂直居中
2019/07/11 HTML / CSS
世界著名的顶级牛排:Omaha Steak(奥马哈牛排)
2016/09/20 全球购物
美国玛丽莎收藏奢华时尚商店:Marissa Collections
2016/11/21 全球购物
会计师职业生涯规划范文
2014/02/18 职场文书
留学生求职信
2014/06/03 职场文书
2014年基层党建工作总结
2014/11/11 职场文书
简单的离婚协议书范本
2014/11/16 职场文书
2016年暑期社会实践活动总结报告
2016/04/06 职场文书
解决thinkphp6(tp6)在状态码500下不报错,或者显示错误“Malformed UTF-8 characters”的问题
2021/04/01 PHP