父子頁面之間的通訊,如果同源的通過 window對象直接調用父(子)中的方法與對象(全局方法與對象)。如果出現跨域的,多系統情況下這種就被同源策略禁止掉了。因此需要使用其他方法解決。可以使用 window.postMessage 方法,附上技術規範 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
vue 等工程也同樣適用
案例
由於 postMessage 是單向的調用,是沒有回調概念的,下面的案例自行封裝了一套代碼,父頁面發送請求後,子頁面處理完成後即可回調函數,而不需要手動聲明。
parent.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>parent.html</title>
</head>
<body>
<span>parent.html</span>
<br>
<br>
<br>
<iframe src="./child.html"></iframe>
<br>
<br>
<button onclick="callChild()">調用子頁面</button>
</body>
<script>
let callChild = function () {
window._postMessage("childFunction", "my is params", function (data) {
alert("parent: callChild childFunction(), return "+data);
console.log("p:" + data);
})
}
/**
* 支持直接對子頁面進行調用,而且子頁面處理完成會回調 _callback 方法
* @param _method iframe中的函數
* @param _params 參數
* @param _callback 調用 _method 成功後返回的數據
* @private
*/
window._postMessage = function (_method, _params, _callback) {
let messageBody = {
method: _method,
params: _params,
}
if (_callback != undefined) {
let proxyMethod = "proxy" + parseInt(Math.random() * 1000);
//用於回調當前頁面的方法使用
messageBody.returnMethod = proxyMethod;
window[proxyMethod] = function (_p) {
try {
_callback(_p);
} finally {
delete window[proxyMethod];
}
}
}
//TODO 這裏的對象需要按需求修改
let iframeWin = document.getElementsByTagName("iframe")[0].contentWindow;
iframeWin.postMessage(messageBody, '*');
}
/**
* 監聽其他地方調用的對象信息,由於回調
*/
window.addEventListener("message", function (event) {
let data = event.data;
if (null == data || data.method == undefined) {
return;
}
//判斷是調用者還是被調用者的數據返回(存在的話就意味着是別人調用你,需要執行完成本地方法後執行回調)
if (data.returnMethod != undefined) {
let returnData = window._callActualMethod(data.method, data.params);
//回調
window._postMessage(data.returnMethod, returnData);
} else {
//這裏是直接調用自身的方法(前面調用的時候已經將此方法丟到window裏面了這裏回調就可以調用到 window[proxyMethod] 方法)
window._callActualMethod(data.method, data.params);
}
});
window._callActualMethod = function (_method, _params) {
// console.log(data.method);
try {
//這裏可以去實現具體調用的方法是在 vue 作用域還是其他作用域中的函數
return window[_method](_params);
} catch (e) {
console.warn(e);
}
}
window.parentFunction = function () {
return "parent";
}
</script>
</html>
child.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>child.html</title>
</head>
<body style="background-color: red">
<span>child.html</span>
<br>
<br>
<button onclick="callParent()">調用父頁面</button>
</body>
<script>
var callParent = function () {
window._postMessage("parentFunction", "my is params", function (data) {
alert("child: callParent parentFunction(), return "+data);
console.log("c:" + data);
})
}
//支持直接對子頁面進行調用,而且子頁面處理完成會回調 _callback 方法
window._postMessage = function (_method, _params, _callback) {
let messageBody = {
method: _method,
params: _params,
}
if (_callback != undefined) {
let proxyMethod = "proxy" + parseInt(Math.random() * 1000);
//用於回調當前頁面的方法使用
messageBody.returnMethod = proxyMethod;
window[proxyMethod] = function (_p) {
try {
_callback(_p);
} finally {
delete window[proxyMethod];
}
}
}
//TODO 這裏的對象需要按需求修改
let iframeWin = window.parent;
iframeWin.postMessage(messageBody, '*');
}
/**
* 監聽其他地方調用的對象信息,由於回調
*/
window.addEventListener("message", function (event) {
let data = event.data;
if (null == data || data.method == undefined) {
return;
}
//判斷是調用者還是被調用者的數據返回(存在的話就意味着是別人調用你,需要執行完成本地方法後執行回調)
if (data.returnMethod != undefined) {
let returnData = window._callActualMethod(data.method, data.params);
//回調
window._postMessage(data.returnMethod, returnData);
} else {
//這裏是直接調用自身的方法(前面調用的時候已經將此方法丟到window裏面了這裏回調就可以調用到 window[proxyMethod] 方法)
window._callActualMethod(data.method, data.params);
}
});
window._callActualMethod = function (_method, _params) {
// console.log(data.method);
try {
//這裏可以去實現具體調用的方法是在 vue 作用域還是其他作用域中的函數
return window[_method](_params);
} catch (e) {
console.warn(e);
}
}
window.childFunction = function () {
return "child";
}
</script>
</html>