第122题 如何实现网页多标签tab通讯

  • 通过websocket
    • 无跨域限制
    • 需要服务端支持,成本高
  • 通过localStorage同域通讯(推荐)
    • 同域AB两个页面
    • A页面设置localStorage
    • B页面可监听到localStorage值的修改
  • 通过SharedWorker通讯
    • SharedWorkerWebWorker的一种
    • WebWorker可开启子进程执行JS,但不能操作DOM
    • SharedWorker可单独开启一个进程,用于同域页面通讯
    • SharedWorker兼容性不太好,调试不方便,IE11不支持

localStorage通讯例子

    <!-- 列表页 -->
    <p>localStorage message - list page</p>
    
    <script>
      // 监听storage事件
      window.addEventListener('storage', event => {
        console.info('key', event.key)
        console.info('value', event.newValue)
      })
    </script>
    <!-- 详情页 -->
    <p>localStorage message - detail page</p>
    
    <button id="btn1">修改标题</button>
    
    <script>
      const btn1 = document.getElementById('btn1')
      btn1.addEventListener('click', () => {
        const newInfo = {
          id: 100,
          name: '标题' + Date.now()
        }
        localStorage.setItem('changeInfo', JSON.stringify(newInfo))
      })
    
      // localStorage 跨域不共享
    </script>

SharedWorker通讯例子

本地调试的时候打开chrome隐私模式验证,如果没有收到消息,打开chrome://inspect/#workers => sharedWorkers => 点击inspect

    <p>SharedWorker message - list page</p>
    
    <script>
      const worker = new SharedWorker('./worker.js')
      worker.port.onmessage = e => console.info('list', e.data)
    </script>
    <p>SharedWorker message - detail page</p>
    <button id="btn1">修改标题</button>
    
    <script>
      const worker = new SharedWorker('./worker.js')
    
      const btn1 = document.getElementById('btn1')
      btn1.addEventListener('click', () => {
        console.log('clicked')
        worker.port.postMessage('detail go...')
      })
    </script>
    // worker.js
    
    /**
     * @description for SharedWorker
     */
    
    const set = new Set()
    
    onconnect = event => {
      const port = event.ports[0]
      set.add(port)
    
      // 接收信息
      port.onmessage = e => {
        // 广播消息
        set.forEach(p => {
          if (p === port) return // 不给自己广播
          p.postMessage(e.data)
        })
      }
    
      // 发送信息
      port.postMessage('worker.js done')
    }

连环问:如何实现网页和iframe之间的通讯

  • 使用postMessage通信
  • 注意跨域的限制和判断,判断域名的合法性

演示

    <!-- 首页 -->
    <p>
      index page
      <button id="btn1">发送消息</button>
    </p>
    
    <iframe id="iframe1" src="./child.html"></iframe>
    
    <script>
      document.getElementById('btn1').addEventListener('click', () => {
        console.info('index clicked')
        window.iframe1.contentWindow.postMessage('hello', '*') // * 没有域名限制
      })
    
      // 接收child的消息
      window.addEventListener('message', event => {
        console.info('origin', event.origin) // 来源的域名
        console.info('index received', event.data)
      })
    </script>
    <!-- 子页面 -->
    <p>
      child page
      <button id="btn1">发送消息</button>
    </p>
    
    <script>
      document.getElementById('btn1').addEventListener('click', () => {
        console.info('child clicked')
        // child被嵌入到index页面,获取child的父页面
        window.parent.postMessage('world', '*') // * 没有域名限制
      })
    
      // 接收parent的消息
      window.addEventListener('message', event => {
        console.info('origin', event.origin) // 判断 origin 的合法性
        console.info('child received', event.data)
      })
    </script>

效果

Last Updated:
Contributors: leeguooooo