Iframe发送消息 postMessage

149 阅读1分钟

前言

最近产品要求开发了一个pc端预览移动端的页面,要求移动端的预览页需要是真实的移动端页面;且移动端预览页上需要响应pc端上的各种操作,例如移动端里的某个节点隐藏或显示,或者是pc端上配置数据变了,移动端预览页也需要同事响应;好吧,那就产品说啥就是啥,干就完事了╮(╯▽╰)╭;考虑到pc端用的是UI框架是antd,移动端用用的ui框架是antd-mobile写的,最终决定用Iframe来预览移动端的页面;

采用Iframe的方式来预览移动端页面的话,面临几个问题,

  1. 父容器的页面和子容器页面之间数据的同步
  2. 父容器和子容器的跨域通讯问题

为了解决这几个问题,查询了Iframe 的相关资料,发现利用postMessage可以进行iframe跨域通信,postMessage可以解决父容器和子容器之间的信息通讯问题;

1. 父容器接收子容器的信息
useEffect(() => {
  window.addEventListener('message', receiveMessgeFromSun, false);

  return () => {
    window.removeEventListener('message', receiveMessgeFromSun);
  };
}, [visible]);

const receiveMessgeFromSun = (e) => {
  if (e.data?.type === 'layout-change') {
    const { current, layouts, curTabsWidget = {} } = e.data?.data || {};
    onLayoutChange(current, layouts, curTabsWidget);
  }

  if (e.data?.type === 'hide-widget') {
    const { hideItem } = e.data?.data || {};
    toHideWidget(hideItem);
  }
};
2. 子容器接收父容器信息
useEffect(() => {
  if (dashboardId === undefined) {
    window.addEventListener('message', receiveMessgeFromFather, false);
  }

  return () => {
    if (dashboardId === undefined) {
      window.removeEventListener('message', receiveMessgeFromFather);
    }
  };
}, []);

const receiveMessgeFromFather = e => {
  if (e.data?.type === 'widgets-change') {
    const { widgetItem, widgets, baseConfig } = e.data?.data || {};
    receiveQueryByDefault(widgets);
    widgets && setWidgets(widgets);
    widgetItem && setWidgetItem(widgetItem);
    baseConfig && globalStyleDetail(baseConfig);
  }
};
3. 子容器向父容器发送信息
const sendMessageToFather = ({type, data}) => {
  window.parent.postMessage({
    type,
    data
  }, '*');
};
4. 父容器向子容器发送信息
// 给Iframe发送消息
sendMessageToIframe = ({type, data }) => { console.log(type, data);
  this.iframeRef.current.contentWindow.postMessage({
    type,
    data
  }, '*');
}