Vue + iframe 的一些记录
最近倒了一个新公司,进公司第一天就傻眼了,全公司300号人、系统后端团队10人,前端团队1人!1人!1人!,没错就是我。
心里哇凉哇凉的,公司项目全是cms类的多标签夜管理系统(6个),现在需要整一个门户系统,将这些系统整合进来,统一 一个入口。交代完背景,开始我们的踩坑之路。
先看眼美女调整下心情~
问题:
- 门户系统跟子系统(iframe 嵌套页面)通信问题(子系统需要从门户系统获取token令牌)
- dev环境下子系统需要保留原先的模式,保留左侧菜单+多标签页面(生产环境下隐藏)
- iframe 浏览器后退问题
- 子页面弹框遮罩层只覆盖到iframe区域
填坑:
- 在门户系统设计好路由及从路由获取子系统url给到iframe正常访问的前提下,我们需要在iframe 刚开始加载的时候,把 token、及 子系统需要的菜单、菜单权限数据给到子系统。
由于门户和 iframe 页面存在跨域问题,所以无法将数据挂载到window对象下、或者localStorage下进行数据交互。
所以这里我们采用
window.postMessage(message, targetOrigin,[transfer]);
查询MDN介绍: window.postMessage 跨越通信
语法:otherWindow.postMessage()
注意、敲重点:otherWindow ! otherWindow ! otherWindow !
由于年轻,一上来就用 window.postMessage 导致两个系统都不能收到彼此发的数据。
这里是指 给那个窗口发数据,就使用哪个窗口的window对象 .postMessage。
我们这里是 主框架+iframe的形式,所以应该这样调用
主框架:获取iframe的window对象再postMessage
const iframeWin = this.$refs.iframeRef.contentWindow;
iframeWin.postMessage(data, "*")
子页面:使用top顶层窗口对象
top.postMessage(data, "*")
看到这里是不是已经了解了基本使用方法了,ok、接着看这个方法带来的问题~
实际使用时、我在主框架路由变化,子系统挂载前(main.js文件)使用postMessage通知主框架给它发送数据,子系统对message进行监听,获取token数据。
子系统:
main.js
window.addEventListener("message", function(e) {
window.isFrame = true // 用来区分是否用门户框架访问
setToken(e.data.token) // 本地存储
}, false)
top.postmessage({ type: "isReady"}, "*") // 通知主框架发送token,被上面监听方法获取
new Vue({
el: "#app"
})
主框架:
content.vue
create() {
window.addEventListener("message", function(e){
if (e.data.type === 'isReady') {
this.$refs.iframeRef.contentWindow.postMessage({type: '', token:'aaa'},"*")
}
}, false)
}
发现token并不能按我设想的在挂载前被存储并在子系统挂载后被各个api接口所使用,api调用时token:null。经过一番折腾,才知道一切是因为:
postMessage 这个方法是一个异步的方法
涉及微任务、宏任务、自行查找资料
总之上面的写法postMessage无法再Vue实例创建前将token存储到本地。
我们对子页面的代码做下调整:
子页面:
main.js
window.addEventListener("message", getData, false)
function mounted() {
new Vue({}) //挂载vue实例
}
function getData(e) {
window.isFrame = true // 用来区分是否用门户框架访问
setToken(e.data.token) // 本地存储
mounted() // 收到数据再挂载vue实例
}
if(process.env.NODE_ENV === "production") {
top.postmessage({ type: "isReady"}, "*")
} else {
mounted() // dev 环境下直接挂载
}
2.对于子系统保留左侧菜单+多标签页面(生产环境下隐藏)
上文中我们已经设置了变量区分是从门户框架访问还是直接访问子系统---window.isFrame
所以只需要设置个计算属性,然后切换dom的class即可
3.前进后退通过多iframe的形式控制对应menu的iframe显示隐藏即可
4.在弹框打开事件中通过postmessage通知父级弹出遮罩层