网络之postMessage通信

1. 背景

为了安全考虑,浏览器会有同源策略限制,禁止跨域访问数据。但是具有src属性的标签不受限制,可以跨域访问资源,例如:

1
2
3
4
<img src="" alt="">
<script src=""></script>
<iframe src="" frameborder="0"></iframe>

需要注意的是:link标签也是可以跨域的。

2. 用iframe标签引入同源和不同源页面

用iframe标签引入同源的页面:
如在test文件夹下面有一个parse.html文件和child.html文件,在parse.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<iframe src="./child.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
</html>

在child.html文件中写入:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>child</title>
</head>
<body>
<h1>child page</h1>
</body>
</html>

用iframe标签引入不同源的页面:
如test文件夹下面有一个parse.html文件,parse.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<iframe src="https://www.baidu.com" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
</html>

3. 同源与不同源的取值问题

此时用的是一个同源的:
如下面我们来看一个例子:
test文件夹下面有一个parse.html文件和child.html文件,在parse.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<button class="btn">btn</button>
<iframe src="./child.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
<script>
var oBtn = document.getElementsByClassName("btn")[0];
oBtn.onclick = function(){
console.log(a)
}
</script>
</html>

然后在child.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>child</title>
</head>
<body>
<h1>child page</h1>
<script>
var a = 123;
</script>
</body>
</html>

当你点击btn按钮的时候,控制台会报错,会告诉你a是没有定义的。其原因是因为parse.html中的a是定义在parse下面的window.a,而child.html中的a是定义在child下面的window.a,因此它们是两个a,由于parse中没有a,因此会报一个a未声明的错误。

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<button class="btn">btn</button>
<iframe src="./child.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
<script>
var oBtn = document.getElementsByClassName("btn")[0];
oBtn.onclick = function(){
console.log(window.frames["demo"].a); // 此时就打印出来的是123,window.frames["demo"]表明是把子页面的window对象找到了
}
</script>
</html>

注意:浏览器限制了javascript的权限,当时跨域的时候,不能进行各种的读写。(意思就是同源的可以操作,但是不同源的是不能进行操作的,你只能把页面拿过来,你不能读也不能写)

当我们引入一个跨域资源的话:
比如此时我们把child.html文件推送到chanke的服务器,它的网址为:vip.chanke.xyz/aimee/chanke/web/iframe/demo.html ,然后在parent.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<button class="btn">btn</button>
<iframe src="http://vip.chanke.xyz/aimee/chanke/web/iframe/demo.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
<script>
var oBtn = document.getElementsByClassName("btn")[0];
oBtn.onclick = function(){
console.log(window.frames["demo"].a);
}
</script>
</html>

由于是访问的跨域资源,因此在控制台中就会报错,会报一个跨域的错误,错误信息如下图所示:
跨域错误
因此需要注意:同源的时候才可以取值,而不同源的时候是不能取值的。

4. 父页面与子页面的跨域通信(postMessage)

前提是:父页面与子页面它们两都允许的情况下才能进行一个跨域通信。
解决办法:postMessage方法

4.1 父页面向子页面传送值

如在test文件下面有parse.html文件和child.html文件,parse.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<input type="text" class="input">
<button class="btn">btn</button>
<iframe src="http://vip.chanke.xyz/V02011/chanke/child.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
<script>
var oBtn = document.getElementsByClassName("btn")[0];
var oInput = document.getElementsByClassName("input")[0];
oBtn.onclick = function(){
var val = oInput.value;
window.frames['demo'].postMessage(val,'http:vip.chanke.xyz') // 通过window.frames['demo']找到需要传的子页面,
// 通过postMessage(想传的值,想往哪一个地方传)方法,注意:想往哪一个地方传的意思是:window.frames['demo']的域是哪一个
// 它就是哪一个域
}
</script>
</html>

在child.html中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>child</title>
</head>
<body>
<h1>child page</h1>
<script>
// 此时子页面需要接收一下,通过一个message事件来进行一个相应
// 当postMessage传过来的时候,就会触发message,传过来的都在messageEvent事件对象下面。
window.addEventListener("message",function(messageEvent){
console.log(messageEvent);
})
</script>
</body>
</html>

当如在input框中输入123的时候,此时控制台打印的内容如下图所示:
123控制台打印内容

4.2 子页面向父页面传送值

如在test文件下面有parse.html文件和child.html文件,parse.html文件中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>parent</title>
</head>
<body>
<h1>parent page</h1>
<iframe src="http://vip.chanke.xyz/V02011/chanke/child.html" style="border: 1px solid #ccc;" name="demo"></iframe>
</body>
<script>
// 做一个相应的处理,绑定一个事件来响应子页面给父页面传的值
window.addEventListener("message",function(messageEvent){
console.log(messageEvent);
})
</script>
</html>

在child.html页面中写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>child</title>
</head>
<body>
<h1>child page</h1>
<button class="btn">btn</button>
<input type="text" class="input">
<script>
var oBtn = document.getElementsByClassName("btn")[0];
var oInput = document.getElementsByClassName("input")[0];
oBtn.onclick = function(){
var val = oInput.value;
// 父页面的窗口就叫parent,parent表示是谁引入的child.html页面,这个parent就是谁
// parent.postMessage(传送的值,父页面的源) 什么是源?源就是协议、域名和端口号。
parent.postMessage(val,"http://localhost");
};
</script>
</body>
</html>

当如在input框中输入daipi173的时候,此时控制台打印的内容如下图所示:
child.html页面输入daipi173打印的内容

注意:如果你不想处理任何数据,那么你就没有必要写message事件,不写message事件,谁给你传数据你都不会触发。