简介
话接上回,介绍了Postmate的基础使用 相信大家对Postmate有了一定的了解,也都清楚Postmate通信的基础用法了,接下来讲一下实际情况下使用Postmate 和 会遇到的问题
实际使用Postmate
Postmate主要解决的是使用iframe嵌入页面后,两个页面之间的通信问题,
那还是最实际的举个例子:
这是一个页面需要一个弹窗,当然这个弹窗可以是从外部引入的,所以可以使用到iframe来引入
主页面和这个弹窗都是相对独立的模块,本没有任何联系,现在使用iframe将他们联系到一起,现在他们有了联系就一定需要通信。就比如一个简单的需求,点击截图按钮需要获取这个父页面的视频截图,要怎么做?
在弹窗里面没有办法直接获取父页面的值,更没法调用父页面的方法,这时候就需要Postmate来让他们进行通信
Postmate 父子通信 -- 问题解析 (下面全是讲解,慎入)
上篇文章讲过父子之间如何传值,但是大家有没有遇到一个问题,父页面监听一个事件,当与子页面建立连接之后,子页面立马发布了这个事件,但我们并不一定需要这个时候让他触发事件,就比如我刚刚打开这个弹窗,他就给我截了个图,我还没有点击截图,他就直接截图了,这是不是不太合理。
child
import PostMate from "Postmate";
//创建实例
const handshake = new PostMate.Model({
height: () => document.height || document.body.offsetHeight
});
//点击事件
document.querySelector().addEventListener('click',()=>{
handshake.then(parent => {
parent.emit('someEvent', '这是子页面!!')
});
})
这么写是很符合逻辑的对吧,当点击按钮时,再去触发这个发布事件,在给父页面传递这个信息,告诉他你需要干嘛,这是正常情况下我们需要的,但是你试过就会发现一个问题,你的点击事件无法触发这个parent.emit()事件,你可以打印,但是他就是不会触发,你一定会觉得很奇怪,因为我当时就是这样的
这跟Postmate的握手有关,先不说,这个方法是行不通的,因为他们需要先建立联系,不能你点击后再建立联系,
那该怎么办呢? 这时候你应该会发现,想要发布就需要handshake.then建立联系之后返回的这个对象parent,因为parent.emit可以发布,只要拿到这个parent,就可以实现自由发布了。
完全正确,这也是大多数人的逻辑,因为就是这样,但是要怎么做呢?
我的想法是:在全局创建一个变量,然后把parent赋值给全局变量不就可以拿到了吗,但是事实证明我太天真了,大家都可以去试试,去想想为什么?
import PostMate from "Postmate";
const handshake = new PostMate.Model({
height: () => document.height || document.body.offsetHeight
});
const getParent = null
handshake.then(parent => {
console.log(parent); // 有内容
getParent = parent
console.log(getParent); // 有内容
});
console.log(getParent); //null
在建立联系时,这个变量是确实拿到parent的值了,但是为什么在全局打印的时候又变回null,当时我以为是这个函数内部不允许赋值,但是在里面打印getParent是存在值的,那就说明我的想法不对,但是为什么到外面就没有了呢,难道函数可以组织把内部的值带出来吗?挺扯的吧,但是我当时就这么想过,但事实肯定不会那么离谱。
当时想了很久,也改变了很多种代码的写法,发现都没有结果,发现应该不是代码的问题,是什么没有考虑到,于是想到了,这是用了iframe嵌入的页面,这两个页面是相互独立的,发现没有大家,他们之间并没有什么关联,只是A引用了B,但是B还是自己本身,他本身有的东西,不会消失,而B是什么? 他是一个页面,所以他有自己的作用域,于是:
import PostMate from "Postmate";
const handshake = new PostMate.Model({
height: () => document.height || document.body.offsetHeight
});
handshake.then(parent => {
console.log(parent);
window.getParent = parent
window.getParents = { "text": 'orange' }
});
于是我把这个parent挂载到window底下,兄弟们,发生了什么,window底下竟然没有这个对象,我担心是我代码问题,于是创建了一个新的元素发现也没有,我当时特别激动,因为真相马上就要浮现了,于是我再次证明我的想法,我到父页面挂载一个对象到window下。
import PostMate from "Postmate";
const handshake = new PostMate({
container: document.getElementById('iframe-container'),
url: '../../child/src/child.html',
name: 'my-iframe-name',
classListArray: ["myClass"]
});
handshake.then(child => {
child.on('someEvent', (data) => {
console.log('someEvent', data);
const iframe = document.querySelector('#iframe-container')
iframe.contentWindow.postMessage({
text: '收到!这里是父页面over~'
}, '*');
});
});
window.getParents = { "text": 'orange' } //!!!
在控制台打印出现了我挂载的这个对象getParent,这就说明一个问题,那就是他们两个的window是不同的,所以在子页面挂载的时候,在控制打印不出挂载的对象,因为子页面他有自己单独的window,现在我把父页面的挂载删掉,留下子页面的挂载,于是我又想,要怎么才能挂载到父页面的window下呢,想了很久,于是试了一下parent,因为他们是父子关系嘛,所以:
import PostMate from "Postmate";
const handshake = new PostMate.Model({
height: () => document.height || document.body.offsetHeight
});
handshake.then(parent => {
console.log(parent);
window.getParent = parent
window.parent.getParents = { "text": 'orange' }
});
在控制台打印window
结果:发现真的挂载上去了,那结果就已经出来了,只需要将子页面的属性,挂载到window.parent下,就能挂载到父页面的window下了,于是只需要自定义一个变量,挂载到父页面下,这样就可以随时拿来使用,就完成了点击之后再发布事件的需求了。
总结
import PostMate from "Postmate";
const handshake = new PostMate.Model({
height: () => document.height || document.body.offsetHeight
});
handshake.then(parent => {
window.parent.getParent = parent
});
document.querySelector('.xxx').addEventListener('click',function(){
window.parent.getParent.emit("someEvent",data)
})
将建立联系后返回的对象,挂载到父页面的window下,当点击事件发生时,在发送对应的事件和数据,就完成自由的在iframe嵌套的父子页面传值了~~