React中使用postMessage解决跨域传值及样式更改
简介
在平时开发中,除了与服务端客户端传值之外,我们前端传值也会面临一些传值的问题
- 多窗口之间传值
- 页面与嵌套的iframe消息传递
跨域解决之postMessage方式
发消息方调用 postMessage
方法发送消息 postMessage
是html5引入的API可以更方便、有效、安全的解决这些问题。postMessage() 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
收消息方用 MessageEvent
监听消息。
postMessage(data,origin) 方法接受两个参数
- data:要传递的数据, html5 规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify() 方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
- origin:字符串参数,指明目标窗口的源, 协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,someWindow.postMessage() 方法只会在someWindow所在的源(url的protocol, host, port)和指定源一致时才会成功触发message
- event,当然如果愿意也可以将参数设置为"*",someWindow可以在任意源,如果要指定和当前窗口同源的话设置为"/"。
MessageEvent的属性
- data:顾名思义,是传递来的message
- source:发送消息的窗口对象
- origin:发送消息窗口的源(协议+主机+端口号)
同域/跨域父子页面间通讯
父页面
import React, { Component } from 'react';
import { autobind } from 'core-decorators';
export default class FatherPage extends Component {
componentDidMount() {
// 监听子页面发回的数据
window.addEventListener('message', this.receiveMessage);
// 父页面向子页面发送数据
this.refs.iframe.postMessage({style: '#000'}, '*');
}
@autobind
receiveMessage(e) {
if (e.data) {
console.log('from-son', e.data);
}
}
render() {
return (
<h1>Hello FatherPage!</h1>
<iframe
ref="iframe"
name="iframe"
id="iframe"
className="iframe-box"
style={{ height: 500 }}
src='/son-path'
/>
)
}
}
子页面
可以通过父页面传 style
来更改子页面样式
import React, { Component } from 'react';
import { observable } from 'mobx';
import { autobind } from 'core-decorators';
import { Button } from 'antd';
export default class SonPage extends Component {
@observable bg = '#fff';
componentDidMount() {
// 监听父页面发送的数据
window.addEventListener('message', this.receiveMessage);
}
@autobind
receiveMessage(e) {
if (e.data) {
console.log('from-father', e.data);
if(e.data.style) {
this.bg = e.data.style
}
}
}
@autobind
onTest() {
// 触发父页面的 message 事件 * 代表任何页面都可接收
window.top.postMessage('hi', '*');
}
render() {
return (
<h1 style={{backgroundColor: `${this.bg}`}}>Hello SonPage!</h1>
<Button onClick={this.onTest} type="primary">测试</Button>
)
}
}