Cross Origin Communication Postmessage

项目中需要实现一个通过iframe嵌入到其它网站中的辅助工具,由于两个域间需要大量的数据交互操作,因此最近研究了一下比较可行的跨域通信方案,其中postMessage目测是最好的一个。虽然只有现代的浏览器才支持这个方法,但考虑到在其他需求中我使用addEventListener的Capture模式已经把IE6什么的给排除掉了,因此兼容性反倒不成问题。随手给个例子:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
 
  <body>
    <p></p>
    <div style="border: 1px solid whiteSmoke">
      <iframe src="./postMessageB.html" frameborder="0" style="width: 100%;"></iframe>
    </div>
  </body>

  <script>
    ;(function () {

      var theSong = '白山兮高高,黑水兮滔滔,有此山川之伟大,故生民质朴而雄豪,地所产者丰且美,俗所习者勤与劳,愿以此为基础,应世界进化之洪潮,沐三民主义之圣化,仰青天白日之昭昭,痛国难之未已,恒怒火之中烧,东夷兮狡诈,北虏兮矫骁,灼灼兮其目,霍霍兮其刀,苟捍卫之不力,宁宰割之能逃,惟卧薪而尝胆,庶雪耻于一朝,唯知行合一方为责,无取乎空论之滔滔,唯积学养气可致用,无取乎狂热之呼号,其自迩以行远,其自卑以登高,爱校、爱乡、爱国、爱人类,期终达于世界大同之目标,使命如此其重大,能不奋勉乎吾曹,能不奋勉乎吾曹!'
      var lyrics = theSong.split(',')

      var iframe = document.getElementsByTagName('iframe')[0]

      var sing = function ( song ) {
        document.getElementsByTagName('p')[0].innerHTML = song || '...'
      }

      var chorus = function () {
        if (lyrics.length) {
          sing(lyrics.shift())
          iframe.contentWindow.postMessage(lyrics.shift(), '*')
        }
      }

      var receiver = function ( event ) {
        setTimeout(function () {
          chorus()
        }, 2000)
      }
      window.addEventListener('message', receiver, false)

      window.onload = chorus
    })()
  </script>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <script>
    ;(function () {
      var sing = function ( song ) {
        document.getElementsByTagName('p')[0].innerHTML = song || '...'
      }

      var receiver = function ( event ) {
        setTimeout(function () {
          var lyric = event.data
          if (lyric) {
            sing(event.data)
            event.source.postMessage('...', '*')
          }
        }, 2000)
      }

      window.addEventListener('message', receiver, false)
    })()
  </script>

  <body>
    <p>&nbsp;</p>
  </body>
</html>

DEMO比较简单,就是两个页面轮流地唱校歌(准确地说是A教B唱歌),浏览器打开一下就能看到效果。值得注意的是postMessage方法中可以指定目标域,接受事件中也可以通过event.origin检查发送域,这无疑极大地提高了跨域通信的安全性,具体的介绍可以看MDN的相关文章(传送门)。