關(guān)鍵詞:iframe,跨域,vue
最近的項(xiàng)目中嵌入了外部的iframe惶我,想跨域調(diào)用自己頁面的方法傅联,點(diǎn)擊iframe中的返回按鈕嫂便,返回到父級的上一級頁面拗盒,因?yàn)槭亲约旱捻?xiàng)目是單頁應(yīng)用,所以無法直接使用window.location.href
恰力,這個(gè)需求讓我頭疼了兩天(包括衍生出來的問題)叉谜,解決了這個(gè)問題之后我決定總結(jié)一下,首先從簡單的開始:
-
基本概念:
window.self
: 當(dāng)前窗口自身的引用
window.parent
: 上一級父窗口的引用
window.top
: 最頂層窗口的引用
當(dāng)頁面中不存在 iframe 嵌套時(shí)踩萎,則三者均是當(dāng)前窗口自身的引用停局。
-
同域iframe相互調(diào)用:
- 子頁面調(diào)用父頁面方法:
window.parent.fatherFn();
- 父頁面調(diào)用子頁面方法:
window.sonFrameName.sonFn();
(sonFrameName是iframe的name
值)
- 子頁面調(diào)用父頁面方法:
下面才是重點(diǎn)(一般嵌入iframe的應(yīng)用應(yīng)該都是跨域的吧)
-
跨域iframe相互調(diào)用:
首先要了解html5的api——window.postMessage,我實(shí)現(xiàn)跨域調(diào)用都是基于postMessage方法的。
語法:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
其他窗口的一個(gè)引用香府,比如iframe的contentWindow屬性董栽、執(zhí)行window.open
返回的窗口對象、或者是命名過或數(shù)值索引的window.frames
message
將要發(fā)送到其他 window的數(shù)據(jù)企孩。
targetOrigin
通過窗口的origin屬性來指定哪些窗口能接收到消息事件锭碳,其值可以是字符串"*"(表示無限制)或者一個(gè)URI。 (由于安全原因最好不要使用"*")
transfer
可選參數(shù)(基本用不上勿璃,我也沒看懂官方的解釋??)
接收消息:
在window對象上監(jiān)聽派遣的message擒抛,使用window.addEventListener('message',fn);
或者使用window.onmessage = fn;
推汽,我在項(xiàng)目中使用了后者。無論使用哪種方法都要注意調(diào)用方法結(jié)束后要解綁——window.removeEventListener('message',fn);
歧沪,否則很可能會出現(xiàn)重復(fù)調(diào)用的情況民泵。
這里的fn有一個(gè)event參數(shù),拿到的是一個(gè)叫做MessageEvent的對象槽畔,chrome控制臺輸出是這樣的
MDN上列出了三個(gè)重要的參數(shù)
data
從其他 window 中傳遞過來的對象栈妆。 (即其他頁面發(fā)送過來的消息)
origin
調(diào)用 postMessage
時(shí)消息發(fā)送方窗口的 origin. 這個(gè)字符串由 協(xié)議、“://“厢钧、域名鳞尔、“ : 端口號”拼接而成。 (即頁面的url) 更新:之前沒仔細(xì)看早直,這個(gè)origin就是定義上的意思寥假,如https://www.janshu.com
或者http://www.aaa.com:8088
source
對發(fā)送消息的窗口對象的引用; 您可以使用此來在具有不同origin的兩個(gè)窗口之間建立雙向通信。(按照MDN上的例子可以在接受message的回調(diào)中將source作為回信的對象霞扬,也就是己方頁面)
按照MDN上的要求糕韧,為了避免跨站點(diǎn)腳本攻擊,在接收到消息的回調(diào)中需要對origin進(jìn)行判斷喻圃,如果不是正確的消息來源地址萤彩,需要return。
下面上自己寫的demo:(x.x.x.x均為本機(jī)ip地址)
父頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<button id="btn">toSon</button>
<iframe src="http://x.x.x.x:8082/iframe2.html" frameborder="0" name="son"></iframe>
<body>
<script src="http://x.x.x.x:8081/js/jquery.min.js"></script>
<script>
$(function(){
window.addEventListener('message',function(e){
console.log(e);
})
$('#btn').on('click',function(){
window.son.postMessage('fromFather','http://x.x.x.x:8082/iframe2.html');
})
})
</script>
</body>
</html>
子頁面(判斷了消息來源)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">toFather</button>
<script src="http://x.x.x.x:8082/js/jquery.min.js"></script>
<script>
$(function(){
$('#btn').on('click',function(){
window.top.postMessage('hi','http://x.x.x.x:8081/iframe1.html');
})
window.addEventListener('message',function(e){
if(e.origin!=='http://x.x.x.x:8081'){
return;
}
//父頁面點(diǎn)擊按鈕斧拍,執(zhí)行子頁面的方法
console.log(e.data);
})
})
</script>
</body>
</html>
我目前的項(xiàng)目使用的是vue雀扶,來看看單頁應(yīng)用中的用法,以及解綁的方法(偽代碼)
<template>
<div class="full-width full-height drawOnline fix-ios-scroll containHeader">
<MainHeader title="在線繪圖" :backURL="backURL"></MainHeader>
<iframe :src="src"></iframe>
</div>
</template>
<script>
......
export default {
data(){
return{
backURL:'...'
}
},
mounted(){
let _this = this;
//每次生成的實(shí)例都不同(this不同)肆汹,所以使用addEventListener無法解綁
window.onmessage = function (e) {
if(e.data=='backPhoto'){//從iframe頁面中接收到的消息
_this.iframeBack();
}
}
},
destroyed(){
window.removeEventListener('message',this.iframeBack,false);
},
computed:{
src(){
return '......'
}
},
methods:{
iframeBack(){
this.$router.push(this.backURL);
}
}
......
}
</script>
首先整體流程是在自己的頁面中嵌入了一個(gè)外部的iframe愚墓,在iframe中點(diǎn)擊返回按鈕之后,當(dāng)前頁面關(guān)閉返回上一個(gè)頁面昂勉。iframe內(nèi)點(diǎn)擊的返回按鈕之后浪册,向當(dāng)前頁面發(fā)送了'backPhoto'這個(gè)消息,當(dāng)前頁面接收到這個(gè)消息之后(當(dāng)時(shí)直接采取的判斷消息名而沒有判斷來源路徑)執(zhí)行路由跳轉(zhuǎn)岗照,當(dāng)前實(shí)例包括iframe都會被銷毀村象,因此在銷毀之前執(zhí)行了解綁,這樣可以保證每次進(jìn)入這個(gè)頁面window對象重新監(jiān)聽message,iframeBack這個(gè)方法不會重復(fù)調(diào)用谴返。
一開始我是沒注意到解綁這個(gè)問題的煞肾,測試也沒發(fā)現(xiàn)這個(gè)問題,直到我進(jìn)入這個(gè)頁面測其它的功能點(diǎn)返回的時(shí)候才發(fā)現(xiàn)返回的路由地址不對(因?yàn)槁酚赡┪灿衖d嗓袱,而返回的時(shí)候只會返回到第一個(gè)id的地址),通過調(diào)試發(fā)現(xiàn)iframeBack這個(gè)方法調(diào)用了多次习绢,原因在于每進(jìn)入一次這個(gè)頁面window就多監(jiān)聽了一次message渠抹,所以需要在銷毀實(shí)例的時(shí)候執(zhí)行解綁蝙昙。
而怎么解綁這個(gè)問題也是困擾了我半天,一開始無論怎么解綁都不成功梧却,直到我在某個(gè)論壇看到了一個(gè)解決方法:使用onmessage而不是用addEventListener進(jìn)行綁定奇颠,因?yàn)槊恳淮沃匦律蓪?shí)例之后iframeBack函數(shù)與上一次都不同,所以remove并不能準(zhǔn)確的移除上一次綁定的函數(shù)放航。所幸最終還是皆大歡喜完結(jié)撒花了烈拒。
轉(zhuǎn)載請注明出處http://www.reibang.com/p/17c1cf2f4426