什么是页面之间的数据通讯?
浏览器允许我们打开多个标签页,每个标签页可能是同一个项目的不同子页面,也可能是不同的页面,但是我们可能需要从一个页面去触发事件修改数据,然后另一个页面及时响应,这时候就需要用到页面间的数据通讯。
为什么需要页面间的数据通讯?
在实际的业务开发过程中,我们需要实现多页面间的数据同步,例如我在a页进行一个按钮操作,这个操作会改变某些数据,恰巧b页面页使用了这个数据,那么我希望a操作的时候b页面的这个数据也能同步更新,这时候就需要页面间进行通讯,让b页面能监听到数据变化,然后进行副作用。
常用的页面间的数据通讯有哪些?
常用的数据通讯有localStorage、websocket、WebWorker、SharedWorker、Cookie和iframe。
详细介绍一下这些通讯方式?
1、localStorage
html中新增的localStorage是一个本地存储的功能,它允许在同源的页面之间进行通讯。
// a page
const a = 0;
localStorage.setItem("item",a);
// b page
const b = localStorage.get("item");
console.log(b);
// 控制台输出
0
这是我们在开发过程中最常用的操作,这样我们可以在同源的页面中进行数据通讯。但是如果a页面中有一个按钮会进行a++的操作,这时候我们去更新localStorage的数据,这时候b页面由于已经渲染完成,所以页面不会变化,所以这时候我们需要一个能力去监听localStorage的变化,从而更改页面的展示,这时候就需要用到storage这个api:
// a page
<button id="btn">change</button>
const a = 0;
localStorage.setItem("item",a);
const btn = doucument.getElementById("btn");
btn.addEventListener("click",()=> {
a++;
localStorage.setItem("item",a);
})
// b page
const b = localStorage.get("item");
console.log(b);
window.addEventListener("storage",() =>{
const c = localStorage.get("item");
console.log(c);
})
// 页面加载完成后,控制台输出:
// 0
// 当a页面点击按钮后,控制台输出
// 1
storage这个api可以监听到localStorage的变化,如果这时候我们在b页面在打印一下:
window.addEventListener("storage",(event) =>{
console.log(event);
})
这时候我们就会看到控制台有了新的输出:
StorageEvent {isTrusted: true, key: '__tea_cache_first_2608', oldValue: '0', newValue: '1', url: 'https://juejin.cn/post/7087933110678978573', …}
isTrusted:true
bubbles:false
cancelBubble:false
cancelable:false
composed: false
currentTarget: Window {window: Window, self: Window,
document: document, name: '', location: Location, …}
defaultPrevented:false
eventPhase:0
key:"__tea_cache_first_2608"
newValue: "1"
oldValue:"0"
path: [Window]
...省略更多数据展示
这时候我们可以看见两个很重要的数据,newValue和oldValue,这个像不像我们在Vue中使用watch时,能拿到新值和旧值,然后进行单独的处理一样,这就是localStorage在多页面之间的通讯方式。
localStorage的缺点:
- 必须遵守同源策略;
- 限制了值的大小;
- 只能监听非己页面的变化;
2、cookie
cookie也可以存储数据,他也是同源共享的。 cookie的特点: 1、跨域不共享 2、具有存储空间限制 3、请求会自动携带cookie 我们可以加入定时器功能,模拟实现一个轮训:
// 页面a
<script>
setInterval(() => {
//加入定时器,让函数每一秒就调用一次,实现页面刷新
console.log("cookie",document.cookie)
}, 1000);
</script>
// 页面b
<script>
let btnB = document.getElementById("btnB");
let num = 0;
btnB.addEventListener("click", () => {
document.cookie = `客户端B发送的消息:${num++}`
})
</script>
这种方式会让页面一直存在一个定时器,和localstorage相比,缺少一个监听的事件; cookie的缺点: 1、存储大小有限制; 2、轮训消耗性能; 3、请求会携带cookie;
3、 websocket
websocket是一种网络通讯协议。在ajax出现后,我们可以实现网页局部刷新,这归功于ajax可以进行网络请求,可以让开发者从服务器端获取数据后渲染页面,但是这种请求在服务器端响应后就断开了链接,无法实现服务器端主动向客户端推送数据。websocket协议就弥补了这一缺点,它是一个全双工通信的协议,意味着客户端和服务端可以互相通信,享受平等关系。
websocket的特点 1、保持连接状态,HTTP协议是无状态连接,即请求完毕后就会关闭连接; 2、全双工通信,客户端和服务端平等对待,可以互相通信。 3、建立在TCP协议之上; 4、没有同源共享策略,即可实现跨域共享; 开发者在开发过程中最常用websocket去实现聊天室或者消息广播的功能; 我们首先搭建一个node服务,模拟websocket服务器;
// node js server.js
// npm init
// npm i ws
const clients = [];
const WebsocketServer = require("ws").Server;
const ws = new WebsocketServer({port: 3001});
ws.on("connection",function (client) => {
console.log("有一个客户端链接成功...")
if(clients.indexOf(client) === -1) {
//去重
clients.push(client);
// 当某一个客户端接收到消息之后
client.on("message",function (msg)=> {
console.log("接收到一条新消息!")
clients.forEach(item => {
// 把消息发送给自己以外的其他客户端
if(item !== client) {
item.send(msg.toString())
}
})
})
}
})
// 客户端a index.js
// 简历一个websocket连接
const ws = new WebSocket("ws://localhost: 3001");
// 开启websocket连接
ws.onopen = function () {
console.log('webxocket 连接成功...')
}
ws.onmessage = function (e) {
console.log("接收到消息推送,", e.data);
}
// 客户端b index.js
<button id="sendmessage">发送消息</button>
const ws = new WebSocket("ws://localhost: 3001");
const btn = doucument.getElementById("sendmessage");
btn.addEventListener("click",function () {
ws.send("向其他客户端发送消息");
})
ws.onopen = function () {
console.log('webxocket 连接成功...')
}
// 客户端a,控制台输出
// 服务端发送的消息 客户端B发送的消息:0
// indexb.html:19 服务端发送的消息 客户端B发送的消息:1
// indexb.html:19 服务端发送的消息 客户端B发送的消息:2
// indexb.html:19 服务端发送的消息 客户端B发送的消息:3
// indexb.html:19 服务端发送的消息 客户端B发送的消息:4
// indexb.html:19 服务端发送的消息 客户端B发送的消息:5
这时候如果客户端b点击按钮,会向websocket服务器发送一条消息,这时候就可以在客户端a打印出来了。 websocket优点:可以跨域实现数据的共享。
websocket缺点:需要编写websocket服务器,上手难度增加。
4、iframe
iframe在实际开发中也是被经常用到的,最常用的功能就是在程序中嵌套一个页面,或者是用来模拟实现一个单点登录。 之前的项目中就用到iframe来模拟单点登录,业务逻辑是这样的,在平台上去登录,登录成功后通过平台跳转到其他子系统,这些子系统又是不同域的系统,但是又需要依赖于平台登录后token,这时候我们就通过iframe将数据传递过去。 想要使用iframe进行数据通信,我们需要了解一个重要的api:postMessage。 postMessage api简介: sameWindow.postMessage(message, targetOrigin);
sameWindow是window对象的饮用; window.open/window.opener 使用window.open返回的对象; HTMLIFrameElement.contentWindow iframe元素的contentWindow属性; window.parent 当前窗口的父窗口对象,如果没有返回自身; window.frames 当前窗口的所有直接子窗口;
postMessage的使用:
// 主页面
<iframe src="./iframe.html" id="iframe" frameborder="0" scrolling="no" width="100%"></iframe>
//发送message
var iframe = document.getElementById('iframe')
iframe.contentWindow.postMessage(data, '*')
// 子页面
//监听message事件
window.addEventListener("message", fn, false);//注意ie中事件绑定是attachEvent
//回调函数
function fn(e) {
//e.data中获取到
}
在这个例子中,主页面通过js,使用postMessage向子页面进行通讯,传递一个data对象,在子页面中通过添加事件message进行监听。这一点和localstorage比较像。 这里我们只是向一个iframe进行数据传递,如果我们有很多个iframe,我们都需要传递数据,就可以使用:
sameWindow.iframes.postMessage(data,*);
如果子页面需要向父页面传递数据:
// 子页面
window.parent.postMessage(data, '*');
// 父页面
document.addEventListener("message",function () {
// ......
})
// 父页面的监听方式和子页面一样。
iframe的缺点: 1、不便于代码查找 2、不利于SEO优化,因为主页面加载前,iframe为空白; 3、iframe会阻塞主页面的加载,因为样式和脚本需要额外链入,调用外部页面,需要额外调用css,增加页面额外的请求次数,增加服务器的http请求; 4、产生多个页面,不易管理 5、多数小型的移动设备(PDA 手机)无法完全显示框架,设备兼容性差;
这时平时常用的几种页面通讯方式,各有好处也各有缺点,这取决于你的项目想要什么效果,或者达到什么目的,你可以根据需要自行去选择一种最适合你项目的方式,毕竟最适合你的才是最好的。