技術棧
系統 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 決定跳轉登錄頁或其他操作