让前端飞Web前端之路

postMessage 方法的小坑

2019-05-22  本文已影响0人  文者字清
postMessage

知识点

MDN对postMessage方法的介绍

场景

在很多时候,我们会遇到编辑操作,点击编辑按钮打开一个新页面,在新页面操作完成之后,需要对父页面做操作,比如:列表刷新。这时需要两个窗口通信,通信方式有很多,比如:在父页面window上添加一些callbackId,然后子页面的window.opener[callbackId]调用对应函数。但是为了支持 electron(新的页面的window.opener是一个被electron重写的对象,只有window的少数方法和属性),所以选择 postMessage。想要把 postMessage 和 addEventListener 这种异步通信方式封装成 promise 形式,所以就踩了个坑。

方案&代码


// 方案一:trigger时返回一个promise,在注册的回调函数里resolve,子页面就可以拿到cb的返回值。

  class Talk{

    // 父页面调用的方法 
    register(cb){
      // 打开一个新页面
      window.open('xxx');
      // 监听
      window.addEventListener('message', e => {
        const cbResult = cb(e.data.data);
        // resolve
        e.data.resolve(cbResult);
      })
    }
    // 子页面调用的方法
    trigger = (...args) => {
      return new Promise((res, rej) => {
        // 将 res 传到父页面
        const data = {
          resolve:res,
          data: args
        }
        // 看文档我们知道,postMessage已经支持了复杂类型的数据,而不仅仅是字符串
        // 但是在promise里,还是只能为字符串,具体原因不清楚,这里会想到JSON.stringify
        // JSON.stringify 会过滤掉数据里的函数、Symbol、undefined
        // 所以,这种方式行不通了
        window.opener.postMessage(data);
      })
      
    }

  }

// 方案二:trigger时返回一个promise,子页面发消息 -> 父页面收到消息,再发送cb返回值 -> 子页面收到消息,执行resovle

  class Talk{

    // 父页面调用的方法
    register(cb){
      // 打开一个新页面
      const opener = window.open('xxx');
      // 监听
      window.addEventListener('message', e => {
        const cbResult = cb(e.data.data);
        // 发送消息, 需要调用子页面window的postMessage方法,第二个参数是targetOrigin,* 为不限制
        opener.postMessage(cbResult, '*');
      })
    }
    // 子页面调用的方法
    trigger = (...args) => {
      window.opener.postMessage(...args);
      return new Promise((res, rej) => {
        // 子页面监听事件
        window.addEventListener('message', e => {
          res(e.data)
        })
      })
    }
  }

总结

上一篇下一篇

猜你喜欢

热点阅读