使用iframe踩坑记录 iframe+postMessage
需求:最近在写一个博客的功能模块,技术栈是单独选择的vuepress,完成后想要融合到一个vue工程里,考虑到后期维护的问题,就通过iframe将vuepress打包后的工程页面引入到vue工程的一个页面中,这样iframe的父子级页面之间也要进行信息的传递,比如说传递iframe的子页面的滚动高度给父页面,然后撑开页面高度。
1.iframe的初始高度问题
vue工程的一个组件,也就是iframe的父页面,引入html;这里出现第一个坑,就是iframe的初始高度一定要设置,具体高度值可以预估一下iframe子页面的高度,否则加载的时候就会出现闪屏的现象,体验不好;如果iframe的子页面高度是变化的,也就是说iframe要实现高度的自适应,接下往下看。。。
<div id="iframeBox"> <iframe id="iframeCon" src="https://www.atool.online/article/blog/index.html" width="100%" height="800px"></iframe> </div>
2.postMessage的实现
上面说到iframe要实现高度自适应,就需要借助postMessage来实现了,自己之前没用过这个,找了很多资料说的感觉都不是很好,就自己直接写了。
在iframe的子页面中:函数写在methods中,怎么去触发getScrollHeight()这个函数看你具体的情况。注意一下,postMessage的源路径,想要将信息传递到哪里。
为什么要写定时器?如果页面内容超多,高度值很大,像我的就到了2W多px,需要等到页面加载完成后才能获取滚动高度,延时短了什么的是不能够获取准确的高度值,会造成iframe的父级页面出现双滚动条,影响体验。
最后,在页面销毁之前记得要清除定时器。个人感觉如果只有一个定时器,或者这个setTimeout不是为了实现setInterval的效果,也没有其他的掺杂,是不用清除的。
beforeDestroy() { clearTimeout(this.timer); }, methods: { getScrollHeight() { this.timer = setTimeout(() => { et height = document.body.scrollHeight; window.parent.postMessage(height, "/"); }, 100); } }
在iframe的父级页面中如何接收信息呢? 这里采用事件的监听,每次有子页面传递信息的出现,我们就获取到传递信息中的高度值,然后设置给当前页面。同样的记得移除监听。
created () { window.addEventListener('message', this.setHeightUpdate, false) }, destroyed () { window.removeEventListener('message', this.setHeightUpdate, false) }, methods: { setHeightUpdate () { let iframe = document.getElementById('iframeCon') iframe.height = event.data + 100 // 这里的event就是子页面传过来的信息,可以打印到控制台看 window.scrollTo(0, 0) // 为了回到顶部 } }
我遇到的就是这些了,虽然简单,但是知识呀。。。
iframe使用postMessage传值addEventListener未接收到却收到webpackwarning问题
项目中使用到iframe,iframe跨域通信需要使用postMessage发送消息,addEventListener接收消息。
原先是获取当前窗口的数据发送过去。
const _iframe = document.getElementById('iframeViewer').contentWindow let _obj = res.data.data _obj.type = 'view' _obj.currentProcessInstanceId = row.id let Base64 = require('js-base64').Base64 _obj.processXml = Base64.decode(res.data.data.processXml) _iframe.postMessage(JSON.stringify(_obj), '*')
后来项目需求更新,需要将本来的窗口多加几个菜单,可以进行页面切换。加了新的菜单之后就出现了问题,postMessage发送出了消息,控制台打印出来addEventListener接收到的消息变成了webpack相关。
bug如下
问了大哥,发现问题可能是因为webpack自身会发送postMessage,需要区分接收到的是webpack发的还是自己发的。另外iframe是动态生成的,未加载好就发送postMessage,webpack会帮助发送。(大概是因为这个,具体的不清楚)
问题解决
所以一个是需要在iframe加载好之后发送postMessage,另一个是区分接收到的是webpack发的还是自己发的。
定义 const _iframe = document.getElementById('iframeViewer').contentWindow 获取目标窗口的窗口对象
定义iframeViewer = document.getElementById('iframeViewer') 获取iframe元素对象
然后使用onload事件,window.οnlοad=function(){SomeJavaScriptCode};(onload事件会在页面或图像加载完成后立即发生)
const _iframe = document.getElementById('iframeViewer').contentWindow const iframeViewer = document.getElementById('iframeViewer') let _obj = res.data.data _obj.type = 'view' _obj.currentProcessInstanceId = this.currentProcessInstanceId let Base64 = require('js-base64').Base64 _obj.processXml = Base64.decode(res.data.data.processXml) iframeViewer.onload = () => { console.log('iframeViewer已加载') _iframe.postMessage(JSON.stringify(_obj), '*') }
这样就确保了在iframe加载后再发送postMessage。
在addEventListener接收时,因为postMessage发送的时候传了一个type,所以可以通过type来判断发过来的是哪个event。
window.addEventListener("message", function(event) { _this.parentData = event.data; if (JSON.parse(_this.parentData).type === "view") { _this.frontTask = JSON.parse(_this.parentData).frontTask; _this.currentTask = JSON.parse(_this.parentData).currentTask; _this.backTask = JSON.parse(_this.parentData).backTask; _this.historyTask = JSON.parse(_this.parentData).historyTask; _this.processInstanceId = JSON.parse(_this.parentData).currentProcessInstanceId; _this.createNewDiagram(JSON.parse(_this.parentData).processXml); } })
写好之后记得刷新页面,不然就会像我一样明明已经改好了但还是看不到数据。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持阿兔在线工具。