跨域解决方案之HTML5 postMessage

发表于2015-07-31
评论2 1.2k浏览
  摘要: web是嵌入到手机客户端中的静态页面,为了统计用户行为需要引入ga,但是ga必须是在www下才行,哪怕是localhost,这是矛盾。要解决这种矛盾就必须要解决iframe的跨域通信。 ...
8.jpg

  问题场景:
  web是嵌入到手机客户端中的静态页面,为了统计用户行为需要引入ga,但是ga必须是在www下才行,哪怕是localhost,这就是矛盾。解决方案是在页面中使用iframe,iframe是在另外一个域名下的,然后在iframe中调用ga方法。很显然必须要解决iframe的跨域通信。
  1. var frame=document.getElementById("gaFrame");
  2. document.body.noxss=function(){
  3. var sendData={
  4. "name":"deals_list",
  5. "event":"deals_click",
  6. "e_data":"这是发送的数据"
  7. }
  8. frame.contentWindow.postMessage(sendData,"*")
  9. }

  每次点击指定区域时候向iframe中发送消息,然后在iframe中监听消息数据,发送ga。
  1. var OnMessage = function(e) {
  2. var data=e.data;
  3. ga('send', 'pageview',"/"+data);
  4. }

  5. function init() {
  6. if (window.addEventListener) { // all browsers except IE before version 9
  7. window.addEventListener("message", OnMessage, false);
  8. } else {
  9. if (window.attachEvent) { // IE before version 9
  10. window.attachEvent("onmessage", OnMessage);
  11. }
  12. }
  13. };
  14. init();

  好,实际场景和方法介绍完了,开工学习相关知识。

  window.postMessage

  window.postMessage 是一个用于安全的使用跨源通信的方法。通常,不同页面上的脚本只在这种情况下被允许互相访问,当且仅当执行它们的页面所处的位置使用相同的协议(通常都是 http)、相同的端口(http默认使用80端口)和相同的主机(两个页面的 document.domain 的值相同)。 在正确使用的情况下,window.postMessage 提供了一个受控的机制来安全地绕过这一限制。

  window.postMessage, 调用时,挂起的脚本必须执行完成才会将 MessageEvent 派遣至目标window (例如:如果一个事件处理程序调用了window.postMessage,剩余的事件处理程序就会挂起超时等). MessageEvent 有消息类型, 它被设置为第一个参数值提供给window.postMessage的data属性, 对应的window调用window.postMessage的时候,window.postMessage主文档的来源的origin属性被称为源属性,指哪个调用window.postMessage的窗口。 (事件的其他标准属性都存在与对应的预期值.)

  语法:
  1. otherWindow.postMessage(message, targetOrigin);

  otherWindow
  引用另外一个窗口,比如上面实例中的iframe,contentWindow 是iframe中的window对象。

  message
  发送到其他window中的数据

  targetOrigin
  目标源,可以限定只接收来自某个URL下的数据
  
  监听发送事件
  1. window.addEventListener("message", receiveMessage, false);

  2. function receiveMessage(event)
  3. {
  4. if (event.origin !== "http://example.org:8080")
  5. return;

  6. // ...
  7. }

  data:来自其他window的数据。

  origin:调用postMessage的窗口url。

  source:发送消息窗口的引用。可以使用该方法使来自不同源的窗口进行双向通信。

  安全性
  1. var popup = window.open(...popup details...);

  2. // When the popup has fully loaded, if not blocked by a popup blocker:

  3. // This does nothing, assuming the window hasn't changed its location.
  4. popup.postMessage("The user is 'bob' and the password is 'secret'",
  5. "https://secure.example.net");

  6. // This will successfully queue a message to be sent to the popup, assuming
  7. // the window hasn't changed its location.
  8. popup.postMessage("hello there!", "http://example.org");

  9. function receiveMessage(event)
  10. {
  11. // Do we trust the sender of this message? (might be
  12. // different from what we originally opened, for example).
  13. if (event.origin !== "http://example.org")
  14. return;

  15. // event.source is popup
  16. // event.data is "hi there yourself! the secret response is: rheeeeet!"
  17. }
  18. window.addEventListener("message", receiveMessage, false);
  1. /*
  2. * In the popup's noxsss, running on <http://example.org>:
  3. */

  4. // Called sometime after postMessage is called
  5. function receiveMessage(event)
  6. {
  7. // Do we trust the sender of this message?
  8. if (event.origin !== "http://example.com:8080")
  9. return;

  10. // event.source is window.opener
  11. // event.data is "hello there!"

  12. // Assuming you've verified the origin of the received message (which
  13. // you must do in any case), a convenient idiom for replying to a
  14. // message is to call postMessage on event.source and provide
  15. // event.origin as the targetOrigin.
  16. event.source.postMessage("hi there yourself! the secret response " +
  17. "is: rheeeeet!",
  18. event.origin);
  19. }

  20. window.addEventListener("message", receiveMessage, false);

  在web worker是中使用postMessage和onMessage

  主线程中创建 Worker 实例,并监听 onmessage 事件
  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  4. <title>Test Web worker</title>
  5. <noxss type="text/Javanoxss">
  6. function init(){
  7. var worker = new Worker('compute.js');
  8. //event 参数中有 data 属性,就是子线程中返回的结果数据
  9. worker.onmessage= function (event) {
  10. // 把子线程返回的结果添加到 div 上
  11. document.getElementById("result")[removed] +=
  12. event.data+"<br/>";
  13. };
  14. }
  15. </noxss>
  16. </head>
  17. <body noxss="init()">
  18. <div id="result"></div>
  19. </body>
  20. </html>

  在客户端的 compute.js 中,只是简单的重复多次加和操作,最后通过 postMessage 方法把结果返回给主线程,目的就是等待一段时间。而在这段时间内,主线程不应该被阻塞,用户可以通过拖拽浏览器,变大缩小浏览器窗口等操作测试这一现象。这个非阻塞主线程的结果就是 Web Workers 想达到的目的。

  compute.js 中调用 postMessage 方法返回计算结果
  1. var i=0;

  2. function timedCount(){
  3. for(var j=0,sum=0;j<100;j++){
  4. for(var i=0;i<100000000;i++){
  5. sum+=i;
  6. }
  7. }
  8. // 调用 postMessage 向主线程发送消息
  9. postMessage(sum);
  10. }

  11. postMessage("Before computing,"+new Date());
  12. timedCount();
  13. postMessage("After computing,"+new Date());
 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引