window.postMessage方法可以安全的启用window对象之间的跨源。例如,在页面和它产生的弹出窗口之间,或者在页面和嵌入其中的iframe之间。
一般, 当且仅当它们源自的页面共享相同的协议、主机、端口号(同源策略)时,允许不同页面上的脚本互相访问。但如果跨域(不符合同源策略时),是不允许页面上的脚本互相访问。
postMessage()
transInfoToTargetWindow.postMessage(needTransmitMess, targetOrigin, [transfer])
-transInfoToTargetWindow
是将要接收消息的窗口的引用。获得此引用的方法包括:
--window.open (生成一个新的窗口,然后引用它)。
--window.opener(产生(通过window.open打开的)目标窗口的引用)
--HTMLFrameElement.contentWidow(父窗口嵌入iframe的引用)
--Window.parent(从嵌入式内部引用父窗口<iframe>)
--window.frames+索引值(命名或数字)(window.frames[ 0 ]与document.getElementsByTagName( "iframe" )[ 0 ].contentWindow是相同的)
-needTransmitMess
此参数是要发送到目标窗口的数据,将会自动被结构化克隆算法序列化。这意味着可以将各种各样的数据对象安全的传递到目标窗口,而无需自己序列化。
-targetOrigin
指定要发送数据的源。可以是字符串“*”,表示任意源。也可以是指定的url。如果在发送数据的时候, transInfoToTargetWindow参数的引用与targetOrgin参数提供的内容不匹配,则不会触发此事件。记得要始终提供具体的targetOrigin,而不是使用“*”。是为了防止若未提供特定目标时,会泄露发送的数据到一些恶意网站。
-transfer(可选参数)
是与传递数据一起传输的Transferable对象序列。这些对象的所有权将提供给目标端,并且它们在发送端不下可用。(大多浏览器不支持此参数,不做详述)
目标页面是如何得到传递的数据呢?
目标页面可以通过
window.addEventListener(“message是要传递的数据”,(e)=>{console.log(e)}, false)
方法获得传递过来的属性及相关父页面数据。
e对象下的属性有:
data:源窗口传递给目标窗口时的数据。
orgin:发送消息数据的窗口的url。注意此来源不能保证是该窗口的当前或未来源,因为该窗口可能已经被导航到调用postmessage后的其他位置。
source:对发送消息的window对象的引用;可以使它来建立两个不同来源窗口之间的双向通信。
注意:
安全隐患:
1.如果不希望从其他站点接收消息,请不要weimessage事件添加任何事件监听。
2.若确实要使用message监听事件,请始终使用origin和可能的source属性验证发消息的窗口的身份。(因为这样指定来消息的来源。)
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
if (event.origin !== "http://example.org:8080")
return;
}
postMessage ----iframe嵌入式时使用方式
主页面。此页面既传递数据给子页面,又获取子页面的数据
import React, {Component} from "react";
class Main extends Component {
constructor(props){
super(props);
this.state = {
showValueIframe :'main父页面'
}
}
componentDidMount (){
console.log("父页面的挂载")
window.addEventListener('message', (e)=> {//获取子页面传递回来的数据
console.log(e.data,'main');
this.setState({
showValueIframe: e.data
})
},false);
}
clickIframe = ()=>{
let data ='我是父页面传到子页面的值';
//给子页面传递数据
document.getElementById('iframe').contentWindow.postMessage(data, "http://localhost:3001/");
}
render(){
return(
<div>
{this.state.showValueIframe}
<button onClick = {this.clickIframe}>iframe跨域点击事件</button>
<iframe id = "iframe" src = "http://localhost:3001/" >
</iframe>
</div>
)
}}
export default Main;被嵌入到主页面的js,此页面既传递数据给父页面,又获取父页面的数据
import React, {Component} from "react";
class Secode extends Component {
constructor(props){
super(props);
this.state = {
showValueIframe :'子页面'
}
}
componentDidMount(){
console.log("子页面的挂载")
//获取父页面传递回来的数据
window.addEventListener('message', (e)=> {
console.log(e.data,e,'second');
this.setState({
showValueIframe: e.data
})
},false);
}
clickIframe = ()=>{
window.parent.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");
}
render(){
return(
<div>
{this.state.showValueIframe}
<button onClick = {this.clickIframe}>传输数据给父页面</button>
</div>
)
}}
export default Secode;1.初始页面
2.当点击了父页面的clickIframe方法,传递给子页面
父页面:此处获取的iframe的src上的url要等于postMessage上的目标源。否则按跨域处理。
document.getElementById('iframe').contentWindow.postMessage(data, "http://127.0.0.1:3000")子页面:只有postMessage的目标源对应上,就会监听到传递过来的消息。
window.addEventListener('message', (e)=> {
console.log(e.data,e,'second');
},false);
3.当点击了子页面的clickIframe方法,传递给父页面
子页面:使用window.parent方式传送消息给父页面
window.parent.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");父页面: 只有postMessage的目标源对应上,就会监听到传递过来的消息。
window.addEventListener('message', (e)=> {
console.log(e.data,e,'second');
},false);postMessage ----open打开式使用方式
主页面。此页面既传递数据给子页面,又获取子页面的数据
被嵌入到主页面的js,此页面既传递数据给父页面,又获取父页面的数据
1。当点击了父页面的clickOpen方法,传递给子页面
父页面:此处获取的open的url要等于postMessage上的目标源。否则按跨域处理。
此处需要注意的是,一定要等子页面加载完毕,再去调用postMessage方法,否则,没有加载完的话,就没法触发监听事件了。
let win = window.open("http://localhost:3001/")
setTimeout(()=>{
win.postMessage('我是父页面传到子页面的值open方式打开', "*")
}, 0)//必须等子页面搭建完成在发送
子页面:只有postMessage的目标源对应上,就会监听到传递过来的消息。
window.addEventListener('message', (e)=> {
console.log(e.data,e,'second');
},false);2.当点击了子页面的clickOpen方法,传递给父页面
子页面:使用window.opener方式传送消息给父页面
window.opener.postMessage('子页面传到父页面的值', "http://127.0.0.1:3000/");
父页面: 只有postMessage的目标源对应上,就会监听到传递过来的消息。
window.addEventListener('message', (e)=> {
console.log(e.data,e,'second');
},false);