MessageChannel 使用

480 阅读2分钟
  • 作用MessageChannel作为宏任务使用广泛,比如react的Scheduler调度中就使用了MessageChannel,vue某版本的nextTick原理实现中也是使用了MessageChannel。 上述使用都是为了使用宏任务

  • MessageChannel核心: 利用html5 的postMessage可以跨域传值的特性。

      1.  react中使用
      const channel = new MessageChannel()
      const port = channel.port2
    
      // 每次 port.postMessage() 调用就会添加一个宏任务
      // 该宏任务为调用 scheduler.scheduleTask 方法
      channel.port1.onmessage = scheduler.scheduleTask
    
      const scheduler = {
        scheduleTask() {
          // 挑选一个任务并执行
          const task = pickTask()
          const continuousTask = task()
    
          // 如果当前任务未完成,则在下个宏任务继续执行
          if (continuousTask) {
            port.postMessage(null)
          }
        },
      }
    
  • 使用:const {port1, port2} = new MessageChannel()

    1. 允许我们创建一个新的消息通道,并通过它的两个port1, port2 (MessagePort) 属性发送数据。
    2. port1 与port2 好比打电话时候的两方,它们都可以作为发送方,也都可以作为接受方,取决于使用。
    3. 发送方页面用window.postMessage发送数据(需把另外port对象传递给接受方页面),
    4. 接收方页面通过window.onMessage 来接受发送方页面发来的信息
    5. 接收方页面通过传来的port对象的postMessage方法向发送方页面发送信息
    6. 发送方页面通过对应port.onMessage方法接收另外port发来的信息

    代码如下:

主页面
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width" />
        <title>Channel messaging demo</title>
        <link rel="stylesheet" href="" />
    </head>
    <body>
        <p class="output">My body</p>
        <iframe src="test.html" width="560" height="320"></iframe>
    </body>
    <script>
        var channel = new MessageChannel();
        var output = document.querySelector(".output");
        var iframe = document.querySelector("iframe");

        iframe.addEventListener("load", onLoad);

        function onLoad() {
            channel.port1.onmessage = onMessage;
            iframe.contentWindow.postMessage(
                    "Hello from the main page!",
                    "http://127.0.0.1:5501",
                    [channel.port2] // 接受方可以通过e.ports[0] 取值
            );
        }

        function onMessage(e) {
                output.innerHTML = e.data;
        }
    </script>
</html>

//test.html
<!DOCTYPE html>
<html>
	<head>
            <meta charset="utf-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
            <meta name="viewport" content="width=device-width" />
            <title>My page title</title>
            <link rel="stylesheet" href="" />
	</head>
	<body>
            <p class="output">iFrame body</p>
	</body>
	<script>
            var output = document.querySelector(".output");
            window.addEventListener("message", onMessage);

            function onMessage(e) {
                output.innerHTML = e.data;
                
               // e.ports[0] 存储发送方传来的port对象
                e.ports[0].postMessage("Message back from the IFrame");
            }
	</script>
</html>
  • postMessage:

    postMessage( data , origin , [transfer] ),接受两个参数

    1. data:需要传递的数据,html5规范中该参数可以是JavaScript中的任意基本类型或可复制的对象,但是不是所有浏览器都能完美支持html5,所有还是用JSON将数据序列化比较好。

    2. origin : 目标字符串参数,指明目标窗口。其值可以是字符串“*”(表示无限制)或者一个URI。

    3. transfer:是一串和message同时传递的Transferable对象。这些对象的所有权将被转移给消息的接收方,发送方不再保有所有权。

ps:

  1. postMessage是宏任务(优先级不高的宏任务)

  2. 微任务与宏任务