专栏首页lhyt前端之路不同页面通信与跨域0. 前言1. localstorage2. 玩转iframe3. 非同域的两个tab页面通信4.MessageChannel

不同页面通信与跨域0. 前言1. localstorage2. 玩转iframe3. 非同域的两个tab页面通信4.MessageChannel

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到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的时候就会经常用到,拿到数据进行处理,但不污染原数据。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【译】Google 官方文章——如何去做coder review

    cr(Code review)主要目的在于确保Google 的代码库代码质量越来越好。而所有相关的工具与流程皆是因应这个目的而生。为达到此目的,势必需要做出一连...

    lhyt
  • 名字随便起——es6 Proxy 0. 前言1. 拦截展示结果2. 驼峰命名3. 自定义cgi名字

    类似Object.defineProperty的set和get,拦截set和get操作进行一些其他逻辑。但是proxy操作的是一个新的代理对象,是对原对象的一个...

    lhyt
  • 那个前端写的页面好酷——大量的粒子(元素)的动效实现

    CSS3dObject这个对象,可以让我们像操作threejs对象那样来操作div,使用threejs丰富的api来实现css+div的3d效果。实际上最终效果...

    lhyt
  • TensorFlow2.0 代码实战专栏(四):Word2Vec (Word Embedding)

    使用TensorFlow 2.0实现Word2Vec算法计算单词的向量表示,这个例子是使用一小部分维基百科文章来训练的。

    磐创AI
  • 112.Path Sum(Tree-Easy)

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path suc...

    Jack_Cui
  • Pandas地震数据获取与可视化

    read_html依赖一些库,比如html5lib,lxml,beautiful soup等,如果没有安装会报错。

    用户2183996
  • [Unity3D] Unity3D游戏开发之UGUI实现伤害数值显示

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明...

    bering
  • 博客文章详情页

    首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容。现在让我们来开发博客的详...

    追梦人物
  • 三条有效的实战经验助你成为移动互联架构师!

    架构师的职责应该是立足于技术和业务之间的中间角色或者平衡点, 在针对业务深刻理解的基础上,针对业务中存在诸多变数,挑选适合的技术架构和技术方案。

    Android架构
  • 经典SQL语句大全之基础

    1、说明:创建数据库 CREATE DATABASE database-name

    陈珙

扫码关注云+社区

领取腾讯云代金券