使用场景
需求:vue项目中,在一个页面嵌套另一个页面,并在两个页面数据变动时触发对应页面得数据更新
方案:使用iframe嵌入页面,使用postmessage进行数据通信
知识点
iframe
<iframe src="http://localhost:9002/#/page" frameborder="0"></iframe>
- src
被嵌套的页面的 URL 地址。使用
about:blank值可以嵌入一个遵从同源策略的空白页.
- contentWindow
属性返回当前HTMLIFrameElement的Window对象. 你可以使用这个
Window对象去访问这个 iframe 的文档和它内部的 DOM. 可读属性, 但是它的属性像全局Window一样是可以操作的.
- window.parent
返回当前窗口的父窗口对象,如果一个窗口没有父窗口,则它的
parent属性为自身的引用.如果当前窗口是一个<iframe>,<object>, 或者<frame>,则它的父窗口是嵌入它的那个窗口.
发送消息
otherWindow.postMessage(message, targetOrigin, [transfer]);
- otherWindow
其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames
- message
将要发送到其他 window 的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化
- targetOrigin
rigin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI
- transfer 可选
是一串和 message 同时传递的
Transferable对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
接收消息
window.addEventListener("message", receiveMessageFunction, false);
function receiveMessage(event){
console.log(event)
//{data:xxx; origin: xxx; source: xxx}
}
event消息体的属性有:
- data
从其他window中传递过来得对象
- origin
调用postMessage消息发送方窗口的origin(源=协议+域名+端口)
- source
对发送消息的窗口对象的引用,可以使用此来建立两个不同origin窗口之间的双向通信
代码实现
准备两个服务页面
a:http://localhost:9001/#/home
b:http://localhost:9002/#/page
iframe嵌套并发送消息
将b页面嵌入到a页面,点击发送消息并获取数据,实现:
<template>
<div class="home">
<p @click="postData">http://localhost:9001/#/home 点击发送message</p>
<div class="iframe-wrap">
<iframe class="iframe-content" ref="iframeTestRef" src="http://localhost:9002/#/page" frameborder="0"></iframe>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
mounted () {
window.addEventListener('message', (e) => {
console.log('home get message e:', e.data);
if (e && e.data && e.origin === 'http://localhost:9002') {
const event = e.data
if (event.type === 'indexCodeChange') {
this.indexCodeChange(event.params)
}
}
})
},
methods: {
postData () {
let typeEvent = {
type: 'getTypeData',
params: {
indexCode: 'indexCode',
}
}
this.$refs.iframeTestRef.contentWindow.postMessage(typeEvent, '*')
let deviceEvent = {
type: 'changeDeviceType',
params: {
type: 'ALL'
}
}
this.$refs.iframeTestRef.contentWindow.postMessage(deviceEvent, '*')
},
indexCodeChange (p) {
console.log('indexCodeChange p', p)
}
}
}
</script>
b页面接受数据并验证
<template>
<div class="component">
<p> this is http://localhost:9002/#/page</p>
<p @click='postCode'>indexCode变动,点击发送message</p>
</div>
</template>
<script>
export default {
name: 'Page',
mounted () {
//注册message事件接受消息
window.addEventListener('message', (e) => {
console.log('page get message e:', e);
if (e && e.data && e.origin === 'http://localhost:9001') {
const event = e.data
if (event.type === 'getTypeData') {
this.getTypeData(event.params)
} else if (event.type === 'changeDeviceType') {
this.changeDeviceType(event.params)
}
}
})
},
methods: {
getTypeData (p) {
console.log('getTypeData p', p)
},
changeDeviceType (p) {
console.log('changeDeviceType p', p)
},
postCode () {
let event = {
type: 'indexCodeChange',
params: {
indexCode: 'newIndexCode'
}
}
window.parent.postMessage(event, '*')
}
}
}
</script>
window.parent.postMessage(event, '*')向父页面a反向发送消息
message范围
this.$refs.iframeTestRef.contentWindow.postMessage(typeEvent, '*')定向发送消息使用得是iframe的contentWindow
http://localhost:9002/#/page页面是不会收到消息