iframe页面的数据通信

542 阅读2分钟

前言

iframe数据通信.jpg

通信过程描述:

  1. 父页面监听message(READY_IFRAME_DATA)
  2. iframe通知父页面可以发送数据(parent.postMessage),同时监听message
  3. 父页面收到通知,开始发送数据(iframe.postMessage)
  4. iframe收到数据(AS_IFRAME_DATA)

数据通信使用postMessage/window.addEventListener('message')的方式,通过指定type来确定数据来源

注意postMessage/message是在同一个window配对的

数据传递的一些 type:

  • READY_IFRAME_DATA: iframe作为通知父页面发送数据标志
  • AS_IFRAME_DATA: iframe作为接收数据来源标志
  • FROM_IFRAME_DATA: 父页面接收iframe传递数据

如果只需要父页面->iframe传递数据,只要前面两个type就够了

iframe页面

注意,如果是传递登录态token, iframesrc初始值应该是空的,避免页面加载太快,token在传递过去之前就发出请求

接收数据

做了两件事:

  • parent.postMessage通知父页面可以发送数据
  • 监听message,父页面通过iframe.postMessage传递数据
  • 将接收的数据存到window下,方便页面初始化的地方读取

写在html文件script标签内,尽量早的执行

<script>
/**
 * 作为iframe时的一些处理
 */
if (window.parent) {
  // 通知父页面传递数据
  window.parent.postMessage({ type: 'READY_IFRAME_DATA' }, '*');
  // 监听父页面传递的数据
  window.addEventListener('message', function(e) {
    if (e.data && e.data.type === 'AS_IFRAME_DATA') {
      const messageData = e.data.data;
      window.__as_iframe_data = messageData;
    }
  });
}
</script>

使用数据

比如在main.ts入口或者App组件 mounted执行方法initAsIframeData

  // 作为ifrmae嵌入的数据处理
  initAsIframeData() {
    const iframeData = (window as any).__as_iframe_data;
    if (iframeData) {
      // 写入token到cookie
      iframeData.token && cookie.set(TOKEN_NAME, iframeData.token);
    }
  }

父页面

数据通信

通过监听iframe页面window.onload事件的方式会比较慢,使用框架的话可能会在框架mounted之后触发,这样传递数据就没有作用了

做了两件事:

  • 监听messageiframe通过parent.postMessage通知父页面可以发送数据
  • 通过iframe.postMessage发送数据

发送数据

  • 初始化

父页面在合适的时机初始化,传入iframe元素及需要发送的数据

  /**
   * 初始化
   * @param {*} iframe 
   * @param {*} data 
   */
  init (iframe, data) {
    if (!iframe) {
      console.warn('请确认iframe元素是否存在!');
      return;
    }
    this.iframe = iframe;
    this.messageData = data;
    this.addListener();
  },
  • 发送数据
  /**
   * 发送数据
   * @param {*} data 
   */
  sendData: function (data) {
    const type = 'AS_IFRAME_DATA';
    this.iframe.contentWindow.postMessage({ type, data }, '*');
  },
  • 整体代码
/**
 * 与iframe内页面的数据通信处理
 */
const iframePageTools = {
  iframe: null,
  messageData: null,
  /**
   * 初始化
   * @param {*} iframe 
   * @param {*} data 
   */
  init (iframe, data) {
    if (!iframe) {
      console.warn('请确认iframe元素是否存在!');
      return;
    }
    this.iframe = iframe;
    this.messageData = data;
    this.addListener();
  },
  /**
   * 发送数据
   * @param {*} data 
   */
  sendData: function (data) {
    const type = 'AS_IFRAME_DATA';
    this.iframe.contentWindow.postMessage({ type, data }, '*');
  },
  listenerHandle: function (e) {
    // 监听事件,iframe内页面需要调用
    // window.parent.postMessage({ type: 'READY_IFRAME_DATA' });
    if (e.data && e.data.type === 'READY_IFRAME_DATA') {
      iframePageTools.sendData(iframePageTools.messageData);
    }
  },
  addListener: function () {
    window.addEventListener('message', this.listenerHandle);
  },
  removeListener: function () {
    window.removeEventListener('message', this.listenerHandle);
  },
};

最后

到此结束