一次iframe子页面与父页面的通信

1,400 阅读6分钟

本文作者: 韩庆新

事件回顾:

收到需求,说是要在别人的系统里嵌入目前由我维护的系统,嵌入的逻辑不多,只有一个创建流程。

刚开始觉得没什么,不就是你改改,我改改,你给我url加个参数,我知道是你调用,修改下页面balabal。。。

奈何想法总是赶不上需求。

我要嵌入的页面大概是这样的↓↓↓。一共有三步,切换步骤不刷新页面

image.png

要嵌入到对方的页面大概如下↓↓↓。是在一个弹出的modal框里,第一步是对方的步骤,后三步由我们嵌入。

image.png

对方的需求:步骤条和下方控制按钮放在他们页面。跟我想的完全不一样。于是开启了这次旅程。

分析需求:

既然步骤条在对方页面,那每一步就需要我们提供一个新的路由对应一个新页面去展示,这很容易想到。但我们系统设计之初,这三步是一个连贯的操作,数据会向下流到最后一步才一次性提交给后端。而且下方控制下一步的按钮放在对方页面,所以就算我们提供新页面也满足不了需求。

不同路由不能满足,那同一个路由不同hash呢?这里要感谢【守卫】大哥,给了一个打开新世界的思路。

同一个路由改变hash值。页面确实不会刷新。那么对方点击下一步时,只要动态改变iframe的url hash,我们这里再监听hash值变化,事先约定好特定值,进而来回切换每一个步骤。嗯,似乎一切都是那么美好。但光想是不够的。Task is cheap,show me the code。所以,我简单模拟了一个iframe环境,嵌入我们系统的页面。一切都很顺利。但我忽略了一点。

这三步中的每一步不是想跳就跳的。每一步的表单都有校验阶段,校验失败我们肯定不会进行下一步。所以这就出现了一个问题:我们如何通知父页面本页面校验阶段是否通过呢?因为步骤条在对方页面,所以我们必须保证我们的实际步骤和对方的步骤条保持同步。

变更需求:

经过一系列讨论,我们决定找他们变更他们的需求。然而对方也很爽快,或许是因为对方不想在这个小需求上花太多时间,或许是因为我们这次是两个人,气势十足。

最终决定步骤条和按钮都可以放在我们页面,但是最后一页必须有一个返回按钮来返回他们的页面。

iframe里的按钮如何改变浏览器的路由呢?这里还要感谢【守卫】大哥,真是字如其花名。提出a标签的target属性或许有帮助。点击查看target的四种值及含义,这里不详细讲了。

a标签target属性的_top值可以使a标签在iframe里改变浏览器的路由。

至此,所有问题似乎以对方的妥协而告终。

但这不是架构组的终点。

继续思考:

现在的情况是:步骤条和按钮都在我们页面,就是说我们不需要做太多的修改,只需要在最后一个页面加一个a标签具有特定target值的属性来改变浏览器的URL,以此来实现关闭弹框modal并且返回到对方的列表页。

这里有一个流畅度的问题。本来到第二步突然切换到iframe,页面会有短时间空白,再加上最后一步跳回列表页,又刷新了整个页面,用户体验非常不好,我都感觉不好。

切换到iframe时的空白只能通过我们页面的首屏性能优化或者微前端的方式,但时间紧任务重。性能优化和微前端不可能半天内搞定,所以第二步优化先放弃,那最后一步能不能优化呢?

又想到了hash值。既然动态改变iframe url hash值不会导致iframe里页面的刷新,那么我通过上方提到的a标签来改变父页面的hash值呢?貌似又发现了新大陆。

说干就干,找了两个系统测试了一番,结果跟预想的一样。

马上通知对方开发人员,提出了这个方案,对方也欣然接受。

至此实现了最后一个步骤返回父页面列表页时,无感知关闭弹框modal。

上线后思考:

最后的方案虽然是步骤条和按钮都在我们页面,但根据上面提到的解决无感知关闭弹框的方案,步骤条和按钮完全可以放到对方页面,就像对方一开始提的需求一样。

具体方案是:父页面点击下一步按钮,改变子页面hash值,子页面监听hash值变化,走校验逻辑。

校验合格,通过代码控制a标签点击来改变父页面hash值,父页面监听hash值变化,进而控制步骤条是否到下一步。

校验不合格,则不改变父页面hash值,则步骤条不动。

不过这种方式对于页面原有hash值有侵入。我们两个系统都没有使用hash所以这个方式可行。对于那些原有页面依赖hash展示的系统,这种方式并不适用。

小结:

iframe的父子页面通信,除了postMessage等方式,相互改变hash值也是不错的选择。

父页面通过JavaScript改变iframe的src的hash,同时监听自己的hash变化。

子页面通过下方的方式改变父页面的hash,同时监听自己的hash变化。

image.png

以此可以延伸到更多的场景。当父子页面需要传送很复杂的数据,可以把数据转成字符串放到hash上。

注意:

hash变化会在历史记录生成一条数据,导致用户回退时的页面可能与预期不符,所以这个方式要视情况使用。

同时还要注意,hash功能本身是为了使浏览器能定位到页面特定的元素↓↓↓

image.png

所以改变hash时要注意页面上不能有有反应的元素。