Vue3与iframe通信方案详解:本地与跨域场景

ps:本项目使用的vue3技术栈

Vue3与iframe通信方案详解:本地与跨域场景

本文详细介绍了在Vue3项目中,与内嵌iframe(包括本地HTML文件和服务端跨域HTML)进行双向通信的完整解决方案。核心通信方式为postMessage API,并针对不同场景提供了安全可靠的代码示例。

1. iframe加载本地HTML文件

1.1 Vue端通信代码

js 复制代码
<template>
...
    <iframe
        ref="iframe"
        name="iframe-html"
        src="./index.html"
        width="100%"
        height="100%"
        frameborder="0"
    ></iframe>
...
</template

如何在vue端跟iframe端加载的.html文件进行通讯呢,看下面的代码

js 复制代码
// vue端
...
const sendMsg2iframe = (msg) => {
    window["iframe-html"].sendMsg2iframe(msg);
}
...
// index.html
...
window.sendMsg2iframe = function (msg) {
    // 接收到vue端发来的消息
}
...

1.2 iframe端(index.html)通信代码

js 复制代码
// index.html
function sendMessageToVue(messageData) {
    // 发送消息到父窗口
    window.parent.postMessage(messageData, window.location.origin);
}

// vue端
// 组件挂载时开始监听消息
onMounted(() => {
  window.addEventListener('message', handleReceiveMessage);
});

// 组件卸载时移除监听,防止内存泄漏
onUnmounted(() => {
  window.removeEventListener('message', handleReceiveMessage);
});

// 接收来自iframe消息的处理函数
const handleReceiveMessage = (event) => {
  // 重要:在实际应用中,应验证event.origin以确保安全
  // if (event.origin !== '期望的源') return;
  
  console.log('Vue组件收到来自iframe的消息:', event.data);
  // 在这里处理接收到的数据
};

2. iframe加载服务器HTML(跨域场景)

其实还是通过window的postMessage进行通讯,只不过是涉及到了跨域问题,下面是具体的代码,关键在于postMessage的第二个参数上

2.1 html端通信代码

js 复制代码
// .html
...
// 获取url并解析出父窗口的origin
const urlParams = new URLSearchParams(window.location.search);
const parentOrigin = urlParams.get('parentOrigin') || window.location.origin;
// 监听来自父窗口的消息
window.addEventListener('message', function (event) {
    if (event.origin === parentOrigin) {
        console.log('收到来自父窗口的消息:', event.data);
        if(event.data.type === 'sendJSON2Unity'){
            window.SendJSON2Unity(event.data.data);
        }
    }
});
function sendMessageToVue(messageData) {
    // 发送消息到父窗口
    window.parent.postMessage(messageData, parentOrigin);
}
...

2.2 Vue端通信代码

js 复制代码
// .vue
...
<iframe
    ref="iframeRef"
    name="unity-home"
    :src="violationDocumentURL"
    width="100%"
    height="100%"
    frameborder="0"
    @load="onIframeLoad">
</iframe>
...
// 这里把自己的origin通过URL参数传给iframe
const violationDocumentURL = import.meta.env.VITE_U3D_SERVICE + "具体路径" + "?parentOrigin=" + encodeURIComponent(window.location.origin);

const iframeRef = ref(null);
const iframeOrigin = ref(import.meta.env.VITE_U3D_SERVICE.replace(/\/$/, ""));  // iframe加载的资源的origin
const sendToUnity = (data) => {
    iframeRef.value.contentWindow.postMessage(
        data,
        iframeOrigin.value
    );
};

// 组件挂载时开始监听消息
onMounted(() => {
  window.addEventListener('message', handleReceiveMessage);
});

// 组件卸载时移除监听,防止内存泄漏
onUnmounted(() => {
  window.removeEventListener('message', handleReceiveMessage);
});
// 接收来自iframe的消息
const handleMessageFromIframe = (event) => {
    // 确保消息来自可信的来源
    if (event.origin === iframeOrigin.value) {
        if (event.data) {
            // do something
        }
    }
};

ok基本就是这样的

3 服务器HTML端(Unity WebGL示例)

因为我们是加载的unity的webgl包,所以最后附赠一下打出的webgl包的index.html的代码(ps:是不压缩版的)

js 复制代码
<!DOCTYPE html>
<html lang="en-us" style="width: 100%; height: 100%">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>Unity WebGL Player | NanDingGDS</title>
	</head>
	<body id="unity3d-body" style="text-align: center; padding: 0; border: 0; margin: 0; width: 100%; height: 100%; overflow: hidden">
		<canvas id="unity-canvas" style="background: #231f20"></canvas>
		<script>
			/** unity的web包加载逻辑开始 */
			const canvas = document.getElementById("unity-canvas");
			const body = document.getElementById("unity3d-body");
			const { clientHeight, clientWidth } = body;

			if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
				var meta = document.createElement("meta");
				meta.name = "viewport";
				meta.content = "width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes";
				document.getElementsByTagName("head")[0].appendChild(meta);
				container.className = "unity-mobile";
				canvas.className = "unity-mobile";
			} else {
				canvas.width = clientWidth;
				canvas.height = clientHeight;
			}

			const baseUrl = "Build/webgl";
			var loaderUrl = baseUrl + ".loader.js";
			var myGameInstance = null;
			var script = document.createElement("script");
			script.src = loaderUrl;
			var config = {
				dataUrl: baseUrl + ".data",
				frameworkUrl: baseUrl + ".framework.js",
				codeUrl: baseUrl + ".wasm",
				streamingAssetsUrl: "StreamingAssets",
				companyName: "DefaultCompany",
				productName: "FanWeiZhang",
				productVersion: "0.1.0",
			};
			script.onload = () => {
				createUnityInstance(canvas, config, (progress) => {}).then((unityInstance) => {
					myGameInstance = unityInstance;
					sendMessageToVue({
						type: "unityLoaded",
						message: "Unity3D加载完成",
					});
				});
			};
			document.body.appendChild(script);
			/** unity的web包加载逻辑结束 */

			// 获取url并解析出父窗口的origin
			const urlParams = new URLSearchParams(window.location.search);
			const parentOrigin = urlParams.get("parentOrigin") || window.location.origin;
			// 监听来自父窗口的消息
			window.addEventListener("message", function (event) {
				if (event.origin === parentOrigin) {
					console.log("收到来自父窗口的消息:", event.data);
					if (event.data.type === "sendJSON2Unity") {
						window.SendJSON2Unity(event.data.data);
					}
				}
			});
			function sendMessageToVue(messageData) {
				// 发送消息到父窗口
				window.parent.postMessage(messageData, parentOrigin);
			}

			window.SendJSON2Unity = function (str) {
				console.log("发送到Unity的JSON字符串:", str);
				myGameInstance.SendMessage("WebController", "receiveJSONByWeb", str);
			};

			window.QuiteUnity = function () {
				console.log("退出Unity3D");
				sendMessageToVue({
					type: "quitUnity",
					message: "退出Unity3D",
				});
			};
			// window.js2Unity = function (str) {
			// 	// 第一个参数是unity中物体的名称,第二是要调用的方法名称,第三个参数是unity中接收到的参数
			// 	// myGameInstance.SendMessage('Main Camera', 'TestRotation', '')
			//     console.log(str);
			// }
		</script>
	</body>
</html>
相关推荐
崔庆才丨静觅14 分钟前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment17 分钟前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅32 分钟前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊34 分钟前
jwt介绍
前端
爱敲代码的小鱼41 分钟前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税1 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore
Cobyte1 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
NEXT061 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法
剪刀石头布啊1 小时前
生成随机数,Math.random的使用
前端
剪刀石头布啊1 小时前
css外边距重叠问题
前端