zoukankan      html  css  js  c++  java
  • 不同页面通信与跨域

    不同页面通信与跨域

    0. 前言

    相信跨域有什么手段,大家都背得滚瓜烂熟了。现在我们来做一些不在同一个tab页面或者跨域的实践。

    1. localstorage

    1.1 onstorage事件

    localstorage是浏览器同域标签共用的存储空间,所以可以用来实现多标签之间的通信。html5出现了一个事件: onstorage,我们在window对象上添加监听就可以监听到变化: window.addEventListener('storage', (e) => console.log(e))

    需要注意,此事件是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数。如果实在是要,自己重写一个方法吧,要不就在修改的时候把自己改的内容po上去。

    示例: js:

    if(!localStorage.getItem('a')){
    	localStorage.setItem('a',1)
    }else{
    	var s = localStorage.getItem('a')
    	localStorage.setItem('a',+s+1)
    }
    window.addEventListener('storage', (e) => console.log(e))
    复制代码

    我们新建两个html分别叫1.html和2.html,并加上上面的js,于是我们每次打开或者刷新该页面就会给a加上1。需要注意的是,如果是双击打开,是在file://协议下的,而且不会触发storage事件,但是会给a加上1,所以可以做一个功能,计算本地某个文件被打开了多少次。如果我们用服务器打开,我们的不同tab页面通信完成了,而且是实时的。

    2. 玩转iframe

    我们都知道frame可以跨域,那么我们来试一下。下面例子,都是一个html内嵌iframe,当然你直接打开iframe那个文件,没什么意义的

    2.1 利用hash变化传递信息实现父子窗口通信(能跨域)

    父窗口:1.html

    html:

    <iframe name="myIframe" src="http://localhost:1000/2.html"></iframe>
    复制代码

    js:

    var originURL = myIframe.location.href
    var i = document.querySelector('iframe')
    i.onload= function(){//这是异步加载的iframe
      i.src += '#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
    }
    复制代码

    子窗口:2.html

    window.onhashchange = function() {
        console.log(window.location.hash)
    }
    复制代码

    我们打开父窗口,发现子窗口的js已经跑起来了。

    既然能跨域,我们直接双击打开1.html,发现还是可以,这个例子双击打开和服务器打开都能达到目的

    2.2 父调用子页面的js或者反过来调用

    父调子:还是基于前面的条件

    var i = document.querySelector('iframe')
    i.onload= function(){
    myIframe.window.f();
    }
    复制代码

    子:2.html

    function f(){
    	console.log('this is 2.html')
    }
    复制代码

    子调父: 子:2.html

    parent.fn1()
    复制代码

    父:1.html

    function fn1(s){
    	console.log('this is 1.html')
    }
    }
    复制代码

    当然,你直接打开2.html是没意义的而且是报错:Uncaught TypeError: parent.fn1 is not a function

    这个需要注意,不能跨域,所以双击打开以及不同域是报错的:Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.,只能服务器打开

    2.3 window.name (能跨域)

    类似于vue、react的prop父子传值,只要在父窗口设置iframe标签的name,在子窗口就可以读到。

    父窗口:1.html

    <iframe  name="myIframe" src="http://localhost:1000/2.html"></iframe>
    复制代码

    子窗口:2.html

    console.log(window.name)
    复制代码

    少年,放心地双击打开吧,有效果的。

    2.4 postmessage(能跨域)

    H5之后为window新增了window.postMessage()方法,第一个参数是要发送的数据,第二个参数是域名。

    父窗口:1.html

    <iframe id="test" name="myIframe" src="http://localhost:1000/2.html"></iframe>
    
    //js
    var frame = document.querySelector('iframe')
    frame.onload = function(argument) {
    	window.frames[0].postMessage('data from html1', '*');
    }
    复制代码

    子窗口:2.html

    window.onmessage = function (e) {
    	console.log(e.data)
    }
    复制代码

    可以跨域,所以能直接双击打开可以看见效果。

    上面的父子窗口,是指一个html里面的iframe标签引入另一个html。

    3. 非同域的两个tab页面通信

    也就是两个毫无关系的tab页面通信(比如我打开一个baidu和一个github),怎么通?

    当然baidu和github能不能通信,我们不知道,得问他们家的开发。前面我们已经知道,iframe能跨域,localstorage能使得两个tab页面通信。那我们就来试一下,iframe桥接两个互不相干的tab页面。注意,bridge是一个html,其他两个tab是指浏览器打开的两个html文件。你可以另外建立两个不同的html,也可以建立两个一模一样的html,然后双击打开也好、服务器打开也好,有两个就可以了。

    下面,我们把桥接的iframe叫做bridge.html吧。我们用node打开,监听本地的1000端口。

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
        <h1>hi</h1>
      </body>
      <script type="text/javascript">
    window.addEventListener("storage", function(ev){
        if (ev.key == 'info') {
            window.parent.postMessage(ev.newValue,'*');
        }
    });
    
    
    window.addEventListener('message',function(e){
        // 接受到父文档的消息后,广播给其他的同源页面
        localStorage.setItem('info',e.data);
    });
      </script>
    </html>
    复制代码

    我们再来两个页面,内容如下。接着我们可以以n种不同方式分别打开,反正是非同源就可以了

    <!DOCTYPE html>
    <html>
    <head>
    	<title></title>
    </head>
    <body>
        <input id="ipt" type="text" name="">
        <button onclick="sub()">sub</button>
        <p id="cont"></p>
        <iframe src="http://localhost:1000/" style="display: none"></iframe>
    </body>
    <script type="text/javascript">
        var ipt = document.querySelector('#ipt')
        function sub(){
          document.querySelector('iframe').contentWindow.postMessage(ipt.value,'*');
          cont.innerHTML +='我:'+ ipt.value + '<br>'
          ipt.value = ''
        }
        window.addEventListener('message',function(e){
          if(e.data) cont.innerHTML +='对方:'+ e.data + '<br>'
          
        });
    </script>
    </html>
    复制代码

    然后一个简单的聊天就搞定了,试试看。加上websocket的话,还可以非同源聊天呢,其他的可以自己随意设置了。

    1

    从1到2的信息实时传递更新就这样子成功了,反之亦然。

    4.MessageChannel

    顾名思义,信息通道。允许我们创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据m,而且在 Web Worker 中可用。可以控制台打印,发现有两个属性,portl1和port2。一个页面内嵌与iframe最常用这种方法。

    就像一条管道,一边出一边进,我们可以给postmessage方法加上第三个参数:

    var channel = new MessageChannel();
    channel.port1.addEventListener("message", function(e){
        window.parent.postMessage(e,'*',[channel.port2]);
        channel = null;
    });
    复制代码

    深拷贝

    n种不同的对象类型?环引用?怎么做到特别容易的深拷?

    然而真的做到了:

    var obj ={a:1,b:2,c:{d:3,e:[{f:1,g:2}]},h:null}
    obj.h = obj
    var res 
    new Promise(resolve => {
        var channel = new MessageChannel();
        channel.port2.onmessage = ev => resolve(ev.data);
        channel.port1.postMessage(obj);
      }).then(data=>{res = data})
    复制代码

    传了数据给管道,管道传回来一个长得一模一样的数据回来,实现了深拷贝。我们叫他结构化克隆,能处理对象循环依赖和大部分的内置对象。比如postMessage发消息给子窗口或者WebWorker的时候就会经常用到,拿到数据进行处理,但不污染原数据。

  • 相关阅读:
    LeetCode "Median of Two Sorted Arrays"
    LeetCode "Distinct Subsequences"
    LeetCode "Permutation Sequence"

    LeetCode "Linked List Cycle II"
    LeetCode "Best Time to Buy and Sell Stock III"
    LeetCode "4Sum"
    LeetCode "3Sum closest"
    LeetCode "3Sum"
    LeetCode "Container With Most Water"
  • 原文地址:https://www.cnblogs.com/caicaizi/p/13684503.html
Copyright © 2011-2022 走看看