Vue3与iframe通信方案详解:本地与跨域场景
ps:本项目使用的vue3技术栈
Vue3与iframe通信方案详解:本地与跨域场景
本文详细介绍了在Vue3项目中,与内嵌iframe(包括本地HTML文件和服务端跨域HTML)进行双向通信的完整解决方案。核心通信方式为postMessage API,并针对不同场景提供了安全可靠的代码示例。
1. iframe加载本地HTML文件
1.1 Vue端通信代码
<template>
...
<iframe
ref="iframe"
name="iframe-html"
src="./index.html"
width="100%"
height="100%"
frameborder="0"
></iframe>
...
</template
如何在vue端跟iframe端加载的.html文件进行通讯呢,看下面的代码
// vue端
...
const sendMsg2iframe = (msg) => {
window["iframe-html"].sendMsg2iframe(msg);
}
...
// index.html
...
window.sendMsg2iframe = function (msg) {
// 接收到vue端发来的消息
}
...
1.2 iframe端(index.html)通信代码
// 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端通信代码
// .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端通信代码
// .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:是不压缩版的)
<!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>