技术栈
系统 A 和系统 B 均使用 vue2
需求
A 中嵌入 B 系统页面,两种情况
1、去掉页面 header、footer,仅保留页面主体
2、保留 header,去掉 footer、header 中 logo、用户信息
禁用 iframe 中滚动,iframe 窗体高度与 B 中页面一致,使用浏览器滚动条
1 隐藏元素
方案制定
域名不同存在跨域,无法在 A 中直接操作 B 中元素,需 B 系统开发者提供支持
iframe 加载之后,给 B 发送消息,B 接收到后将元素隐藏
实现
A 系统,展示 iframe 的 vue 文件
1 <template lang="pug"> 2 .iframe-box 3 iframe(id="iframeInstance" name="iframeInstance" src="http://localhost:8008" scrolling="auto" frameborder="no" seamless ref="iframe" width="100%" height="600") 4 </template> 5 6 <script> 7 export default { 8 async mounted() { 9 await this.$nextTick() 10 this.$refs.iframe.addEventListener('load', this.onIframeLoad) // 给 iframe 绑定监听事件 11 }, 12 13 methods: { 14 onIframeLoad() { 15 console.log('onload') 16 this.$refs.iframe.contentWindow.postMessage({ header: false }, '*') // 加载完成之后发送消息,已知目标地址时不要用*,这里的 false 和 true 对应上述需求中两种情况 17 } 18 }, 19 } 20 </script>
B 系统,App.vue
1 mounted () { 2 window.addEventListener('message', (event) => { 3 // 判断消息来源 4 const arr = ['xxx.com', 'xxxx.com', 'xxx.cn'] 5 const flag = arr.some(item => event.origin.includes(item)) 6 if (!flag) return 7 8 console.log('message', event.data) 9 10 // 如果两个系统不是单点登录同步登录信息,可以接收用户信息后登录 11 if (this.$store.getters.unLogin) { 12 // login 13 } 14 15 // 隐藏 footer 16 const footer = document.querySelector('#app .footer') 17 footer.style.display = 'none' 18 19 // 根据接收的消息隐藏对应元素 20 if (event.data.header) { 21 const logo = document.querySelector('#app .header .logo') 22 const user = document.querySelector('#app .header .user-info') 23 logo.style.display = 'none' 24 user.style.display = 'none' 25 } else { 26 const header = document.querySelector('#app .header') 27 header.style.display = 'none' 28 } 29 }, false); 30 }
2 高度自适应
方案
受跨域限制,B 提供高度,A 接收后调整 iframe height 属性
考虑到展示内容可能收到 tab 切换和过滤条件等影响,通过路由守卫发送消息不满足需求,采用 MutationObserver,监听根元素和子元素
实现
A系统,iframe 展示的 vue 文件
1 <template lang="pug"> 2 .i-iframe 3 iframe(id="iframeInstance" name="iframeInstance" src="http://localhost:8008" scrolling="no" frameborder="no" seamless ref="iframe" width="100%" :height="height") 4 </template> 5 6 <script> 7 export default { 8 data() { 9 return { 10 height: 600, 11 } 12 }, 13 14 async mounted() { 15 await this.$nextTick() 16 this.$refs.iframe.addEventListener('load', this.onIframeLoad) 17 }, 18 19 created() { 20 window.addEventListener('message', event => { 21 const height = event.data.height || 800 22 if(this.height === height) return 23 this.height = height 24 }, false) 25 }, 26 27 methods: { 28 onIframeLoad() { 29 console.log('onload') 30 this.$refs.iframe.contentWindow.postMessage({ header: true }, '*') 31 }, 32 }, 33 } 34 </script>
B 系统,App.vue
1 mounted () { 2 // 可以写成个方法放 methods 里,这里为了方便展示执行时机就不改了 3 const targetNode = document.getElementById('app'); 4 const config = { attributes: false, childList: true, subtree: true }; 5 6 // 加防抖,避免多次触发 7 const callback = debounce(() => { 8 // B 系统设置了 min-height: 100%,过滤条件变化,数据量减少时,由于展示空间足够,高度仍保持上一次的,在这里做一次初始化 9 window.parent.postMessage({ height: undefined }, '*'); 10 11 // 渲染完成后提供最新的页面高度 12 setTimeout(() => { 13 window.parent.postMessage({ height: targetNode.offsetHeight }, '*'); 14 }, 70); 15 }, 300) 16 17 const Mutation = 18 window.MutationObserver || 19 window.WebKitMutationObserver || 20 window.MozMutationObserver; 21 this.observer = new Mutation(callback); 22 this.observer.observe(targetNode, config); 23 }, 24 25 beforeDestroy () { 26 if(this.observer) { 27 this.observer.disconnect() 28 this.observer = null 29 } 30 }
补充
此需求中 A、B 两系统使用单点登录,自动同步登录信息。如有同步登录态信息需求,可参考隐藏元素实现代码
A 传递消息时,增加用户信息。B 接收后执行登录操作
B 系统登出时,传递消息给 A 系统,A 决定跳转登录页或其他操作