vue3中如何使用iframe嵌入vue2頁面
vue3使用iframe嵌入vue2頁面
vue3中使用iframe嵌入vue2頁面或vue頁面和iframe頁面的相互傳參問題
記錄一個開發(fā)過程中遇到的問題,Vue3項目中嵌入了一個iframe,iframe里面的是一個vue2項目的頁面,是項目趕工計較著急,來不及把vue2集成到vue3中,這時候想到用Vue3和Vue2之間來回互調(diào)傳參,下面提供解決思路。
Vue3 父組件
<template> <div class="content"> <iframe id="unityiframe" ref="iframeRef" name="iframeContain" seamless scrolling="yes" :src="srcUrl" style="width: 100%; height: 70vh; border: 0" > </iframe> </div> </template>
<script> import { reactive, toRefs, onMounted, ref,watch, nextTick, getCurrentInstance,} from "vue"; export default { setup() { //接受子組件穿過來的值 function handleMessage(event) { console.log("子頁面?zhèn)鬟^值event", event.data); var datalist = event.data; if (datalist) { sendMessage(); } } // 調(diào)用vue中g(shù)etCurrentInstance方法(此參考V2和V3中this.$refs區(qū)別) const currentInstance = getCurrentInstance(); function sendMessage() { // currentInstance.ctx.$refs是vue3的寫法。vue2中是 this.$refs if (currentInstance.ctx.$refs.iframeRef) { //判斷他不為空,可根據(jù)自己的頁面判斷 currentInstance.ctx.$refs.iframeRef.contentWindow.postMessage( // 向子頁面iframe傳遞 "aaa", //傳遞的參數(shù) "http://10.1.0.238:88/#/workspace/formsPanel" //iframe的頁面地址 ); } } nextTick(() => { window.addEventListener("message", handleMessage); }); return{ handleMessage, currentInstance, sendMessage } }} </script>
Vue2 子組件(1)
mounted() { //給父頁面發(fā)送消息 window.parent.postMessage( "111", "http://10.1.0.238:8080/#/application/function" ); //監(jiān)聽父頁面發(fā)送過來的消息 window.addEventListener("message", this.handleMessageFromParent); }, methods: { // 接收父頁面發(fā)送的消息 handleMessageFromParent(event) { console.log("父頁面?zhèn)鬟^值event", event.data); },
Vue2 子組件(2)
mounted() { //給父頁面發(fā)送消息 window.parent.postMessage( "111", "http://10.1.0.238:8080/#/application/function" ); window.addEventListener("message", (e) => { //接收父頁面發(fā)送的消息 console.log("e",e) }) }, methods: {
vue項目嵌套(vue2嵌套vue3)
實(shí)現(xiàn)思路
- 產(chǎn)品測評通過 iframe 將產(chǎn)品庫加載進(jìn)來。
- 創(chuàng)建 template.vue 中間頁,使用path參數(shù)攜帶產(chǎn)品庫url,并通過傳遞用戶信息和路由信息實(shí)現(xiàn)產(chǎn)品庫自動登陸。
- 使用 postMessgae API 實(shí)現(xiàn)父子項目間的數(shù)據(jù)通信。
實(shí)現(xiàn)步驟
創(chuàng)建 Store/template 模塊,定義產(chǎn)品庫相關(guān)數(shù)據(jù)。
// src/store/modlues/template.ts const state = { sign: 'productLibrary', // 產(chǎn)品庫路由標(biāo)識 routePath: '/template', // 產(chǎn)品庫跳轉(zhuǎn)中間頁path isFullFrame: false, // 是否設(shè)置產(chǎn)品庫Iframe全屏 NODE_ENV: process.env.NODE_ENV, // 環(huán)境變量 ENV_URLS: { development: 'http://127.0.0.1:3000', test: 'http://cms-test.shenlanbao.com', uat: 'http://cms-uat.shenlanbao.com', production: 'http://cms.shenlanbao.com', }, }; const mutations: MutationTree<any> = { SET_isFullFrame(state, val) { state.isFullFrame = val; }, };
創(chuàng)建 template.vue 中間跳轉(zhuǎn)頁路由。
// src/router/modules/page-productLibrary-router.ts { path: '/template', // 對應(yīng)Store/template.ts 的 routePath name: 'productLibrary', // 對應(yīng)Store/template.ts 的 sign cn: '產(chǎn)品庫', hidden: true, component: () => import('@/views/productLibrary/template.vue'), children: [], },
點(diǎn)擊左側(cè)菜單將符合條件的路由跳轉(zhuǎn)到產(chǎn)品庫。
// src/components/common/MenuItem.vue <script> methods: { navigateTo(e): void { const { frontPermission, url } = e; const { sign, routePath } = this.$store.state.template; if (frontPermission.includes(sign)) { if (!url) return; this.$router.push({ path: routePath, query: { path: url }, }); } else { if (this.$route.path === url) return; this.$router.push({ path: url }); } }, } </script>
template.vue 監(jiān)聽路由并更新產(chǎn)品庫路由,同時傳遞用戶登陸信息實(shí)現(xiàn)自動登陸
// src/views/productLibrary/template.vue <div :class="{ wrapper: true, hidemenu: $store.state.template.isFullFrame }"> <iframe :src="src" frameborder="0" class="sub-frame" ref="frames" @load="frameLoad" ></iframe> </div> <script lang="ts"> @Component export default class Index extends Vue { @Ref() readonly frames; src: string = ''; // 產(chǎn)品庫跳轉(zhuǎn)路由 // 監(jiān)聽路由,更新產(chǎn)品庫路由 @Watch('$route') toggleRoute(data) { this.$nextTick(() => { this.frameLoad(); }); } frameLoad() { const { path = '' } = this.$route.query; const auth = localStorage.getItem('auth') || ''; if (!path || !auth) return; this.setSrc(path); // 設(shè)置產(chǎn)品庫跳轉(zhuǎn)路由 const productLibraryRoutes = this.getRoutes(); // 過濾產(chǎn)品庫路由 const permission = { auth, path, userInfo: localStorage.getItem('userInfo'), routes: productLibraryRoutes[0].childPermissionResList, }; // 傳遞用戶登陸信息實(shí)現(xiàn)自動登陸 this.frames.contentWindow.postMessage(permission, '*'); } getRoutes() { const localRoutes = localStorage.getItem('routes') || ''; if (!localRoutes) return ''; const { sign } = this.$store.state.template; // 產(chǎn)品庫跳轉(zhuǎn)路由標(biāo)識 const routes: any[] = JSON.parse(localRoutes) || []; return routes.filter((i) => { return i.frontPermission === sign && i.childPermissionResList; }); } setSrc(path) { const { NODE_ENV, ENV_URLS } = this.$store.state.template; const domain = ENV_URLS[NODE_ENV]; if (path && domain) this.src = `${domain}${path}`; } } </script>
產(chǎn)品庫獲取登陸信息及路由信息進(jìn)行模擬登陸操作。
// index.html <body> <div id="app"></div> <script type="module" src="/src/main.ts"></script> <script> window.addEventListener( 'message', function (event) { const { data } = event if (data.auth) { window.localStorage.setItem('auth', data.auth) window.localStorage.setItem('routes', JSON.stringify(data.routes)) window.localStorage.setItem('userInfo', data.userInfo) window.localStorage.setItem('path', data.path) } }, false, ) </script> </body> // src/router/routerGuards.ts export function createRouterGuards(routers: Router) { routers.beforeEach(async (to, _, next) => { const isFrame = JSON.parse(localStorage.getItem('isFrame') || '') // 是否是 iframe 加載 if (!isFrame) { // 正常登陸操作 } else { const hasToken = localStorage.getItem('auth') || '' const { addRouter } = store.state.global if (hasToken && addRouter) { next() return } localStorage.setItem('auth', localStorage.getItem('auth') || '') await store.commit('global/setRoutes', JSON.parse(localStorage.getItem('routes') || '')) await store.commit('global/setUserInfo', JSON.parse(localStorage.getItem('userInfo') || '')) // 路由添加/標(biāo)記 store.commit('global/isAddRouter', true) const getNewRouter = store.getters['global/getNewRouter'] getNewRouter.forEach((route: Iroute) => { routers.addRoute(route) }) const path = localStorage.getItem('path') to.path = path const newTo = { ...to, name: to.name || undefined, } next({ ...newTo, replace: true }) } }) }
產(chǎn)品庫向產(chǎn)品測試進(jìn)行通信
// src/utils/template.ts // ifarame 內(nèi)嵌時向產(chǎn)品測評窗口進(jìn)行通信事件定義 const source = 'productLibrary' // 產(chǎn)品庫跳轉(zhuǎn)路由標(biāo)識 const posMessage = function (config) { const message = Object.assign({ source }, config) return new Promise((resolve) => { top.postMessage(message, '*') resolve(true) }) } // 是否設(shè)置全屏遮罩 export function postFrameCover(val) { const config = { fnName: 'setIframeCover', params: val, } return posMessage(config) } // 跳轉(zhuǎn)產(chǎn)品測評頁面 export function postTopRouter(path) { if (!path) return const config = { fnName: 'setRoute', params: path, } return posMessage(config) }
問題總結(jié)及解決方案
左側(cè)菜單路由高亮狀態(tài)
展示產(chǎn)品庫頁面時,對應(yīng)左側(cè)菜單路由要相應(yīng)高亮選中。
在監(jiān)聽路由跳轉(zhuǎn)時,判斷當(dāng)跳轉(zhuǎn)產(chǎn)品庫頁面時,取 query 中的 path 字段為 url 做高亮處理。
// src/components/Nav.vue <script> @Watch('$route', { immediate: true }) handlerRouteChange(to: Route): void { this.active = to.path; this.setProductRoute(to); // 判斷產(chǎn)品庫路由 } setProductRoute(to) { const { name = '', query = {} } = to; const { sign } = this.$store.state.template; // 產(chǎn)品庫跳轉(zhuǎn)路由標(biāo)識 const isProductLibRoute = name === sign && query.path; if (isProductLibRoute) { this.active = query.path; } } </script>
產(chǎn)品測評面包屑導(dǎo)航頁面路徑
展示產(chǎn)品庫頁面時,對應(yīng)上方面包屑導(dǎo)航要顯示正確的路由名稱。
在監(jiān)聽路由跳轉(zhuǎn)時,判斷當(dāng)跳轉(zhuǎn)產(chǎn)品庫頁面時,取 query 中的 path 字段為 url 做高亮處理。
// src/components/common/BaseHeard.vue <script> @Watch('$route') $routeWatch(to: Iobj) { this.getBar(to); } getBar(route) { let pathName = this.getRoutePath(route); let name = ''; let barList: Array<Iobj> = []; let pathArr = pathName.split('/').filter((_) => _); let newArr = [ ...this.getNewRouter, ...(this.$router as any).options.routes, ]; for (let i of pathArr) { name = name + `/${i}`; for (let j of newArr) { if (j.path === name) { barList.push({ cn: j.cn, path: j.path, }); break; } } } this.arr = barList; } // 獲取當(dāng)前路由path getRoutePath(route) { let { path: pathName, query } = route; const { routePath } = this.$store.state.template; // 如果產(chǎn)品庫的路由,修改為取query下的path為路由名稱 if (pathName === routePath) { const { path = '' } = query; if (path) return path; } else { return pathName; } } </script>
Dialog彈出窗遮罩覆蓋
當(dāng)點(diǎn)擊彈窗時,通知父窗口,同步修改外層樣式。
// 產(chǎn)品庫 <el-button @click="handleMask()">測試彈窗</el-button> <script> import { postFrameCover } from '@utils/template' handleMask() { postFrameCover(true).then(() => { this.showDialog = true }) } </script> // 產(chǎn)品測評 template.vue setIframeCover(val) { this.$store.commit('template/SET_isFullFrame', val); } // 產(chǎn)品測評 App.vue // 修改左側(cè)菜單的z-index,使遮罩在最頂層 <nav class="nav" :class="{ hidemenu: $store.state.template.isFullFrame }"> <app-nav /> </nav> .hidemenu { background: rgba(0, 0, 0, 0.5); z-index: 100; } // 產(chǎn)品測評 template.vue <div :class="{ wrapper: true, hidemenu: $store.state.template.isFullFrame }"> <iframe :src="src" frameborder="0" class="sub-frame" ref="frames" @load="frameLoad" ></iframe> </div> .hidemenu { background: rgba(0, 0, 0, 0.5); z-index: 100; }
產(chǎn)品庫子項目跳轉(zhuǎn)產(chǎn)品測評頁面
// 產(chǎn)品庫 <el-button type="primary" @click="handleArticle()">跳轉(zhuǎn)產(chǎn)品測評文章詳情</el-button> <script> import { postTopRouter } from '@utils/template' handleArticle() { const route = { path: '/article/articleAdd', query: { id: '795964024911646720', }, } postTopRouter(route) } </script>
產(chǎn)品庫判斷當(dāng)前項目是在iframe中還是web中
// index.html <head> <script> // 判斷是在iframe還是web let isFrame = false if (window.frames.length != parent.frames.length) isFrame = true window.localStorage.setItem('isFrame', isFrame) </script> </head>
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue中使用iframe嵌入網(wǎng)頁,頁面可自適應(yīng)問題
這篇文章主要介紹了vue中使用iframe嵌入網(wǎng)頁,頁面可自適應(yīng)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09vue3+vite assets動態(tài)引入圖片的三種方法及解決打包后圖片路徑錯誤不顯示的問題
這篇文章主要介紹了vue3+vite assets動態(tài)引入圖片的幾種方式,解決打包后圖片路徑錯誤不顯示的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-0356個實(shí)用的JavaScript 工具函數(shù)助你提升開發(fā)效率
今天來看看JavaScript中的一些實(shí)用的工具函數(shù),希望能幫助你提高開發(fā)效率!需要的朋友可以參考下面文章的具體內(nèi)容2021-10-10Vue?關(guān)于$emit與props的使用示例代碼
父組件使用 props 把數(shù)據(jù)傳給子組件,子組件使用 $emit 觸發(fā)父組件的自定義事件,今天通過示例給大家詳細(xì)介紹下Vue?關(guān)于$emit與props的使用,感興趣的朋友一起看看吧2022-03-03vue元素樣式實(shí)現(xiàn)動態(tài)改變方法介紹
vue通過js動態(tài)修改元素的樣式,如果是固定的幾個樣式,我常用的是綁定元素的calss,給不同的class寫好需要的樣式,js控制是否使用這個class2022-09-09vue如何根據(jù)權(quán)限生成動態(tài)路由、導(dǎo)航欄
這篇文章主要介紹了vue如何根據(jù)權(quán)限生成動態(tài)路由、導(dǎo)航欄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03