需求来源
在多数CMS(内容管理系统)后台上,常见的是一个文章列表页面,点击列表项会打开一个新的文章详情页面。编辑人员经常在这个详情页面上对文章操作,比如修改标题、配图、摘要等内容。操作完毕之后,由于文章页和列表页是两个页面,文章内容数据不能及时同步到列表,这样就照成运营人员多次误操作,这大大降低了运营人员的工作效率。
对于前端工程师来讲,实现浏览器多个页卡之间的通信,及时更新相关数据更改,是一件重要的事情。
例如有一个需求:当文章详情页面更新的时候,会同步到文章列表页。
实现方式
一、Cookie+setInterval
cookie最初是在客户端用于存储用户的会话信息的。由于HTTP是一种无状态的协议,服务器单从网络连接上无法知道客户身份。通过cookie就给客户端们颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个cookie。客户端浏览器会把cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该cookie一同提交给服务器。服务器检查该cookie,以此来辨认用户状态。服务器还可以根据需要修改cookie的内容。
在JavaScript中,cookie的操作接口即document.cookie,通过这个接口可以读取、写入、删除cookie。这个操作其实不太友好,所以很多工具库提供了cookie的操作方法。我这里提供一个简单的封装方法。
var QQ = {};
QQ.Cookie={
set:function(name,value,expires,path,domain){
if(typeof expires=="undefined"){
expires=new Date(new Date().getTime()+3600*1000);
}
document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"; path=/")+((domain)?";domain="+domain:"");
},
get:function(name){
var arr=document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));
if(arr!=null){
return unescape(arr[2]);
}
return null;
},
clear:function(name,path,domain){
if(this.get(name)){
document.cookie=name+"="+((path)?"; path="+path:"; path=/")+((domain)?"; domain="+domain:"")+";expires=Fri, 02-Jan-1970 00:00:00 GMT";
}
}
};
cookie有个特性,一个页面产生的cookie能被与这个页面的同一目录或者其他子目录下的页面访问,这样页面之间就产生了一个共享的存储空间。通常把cookie的path设置为一个更高级别的目录,比如默认“/”,从而使更多的页面共享cookie,实现多页面之间相互通信。 cookie所在的域,默认为请求的地址,也可以通过设置document.domain为父域等方式扩大cookie可被访问的域。
实现原理:
列表页通过setInterval定时器循环监听cookie的数据变动
列表页代码:
window.onload=function(){
var tid = '';
setInterval(function(){
if(tid != QQ.Cookie.get("tid")){
alert('数据更新!');
tid = QQ.Cookie.get("tid")
}
}, 1000);
}
当详情页有数据修改时后,写入cookie
详情页代码:
<input id="content" type="text">
<button id="btn">Click</button>
<script>
window.onload=function(){
var oBtn=document.getElementById("btn");
var oInput=document.getElementById("content");
oBtn.onclick=function(){
var val=oInput.value;
QQ.Cookie.set("tid",val);
}
}
</script>
缺点:
- cookie空间有限,浏览器在每一个域名下最多能设置30-50个cookie,容量最多4K左右。
- 每次HTTP请求会把当前域的cookie发送到服务器上,而有些cookie只是浏览器才用的到,浪费网络带宽。
- setInterval的频率设置,过大会影响浏览器性能,过小会影响时效性。
优点:兼容性好,几乎所有的浏览器都支持。
二、localStorage
在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题,localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。
localStorage的API也很简单,提供了JS的读写操作。
if(!window.localStorage){
alert("浏览器不支持localstorage");
return false;
}else{
var storage = window.localStorage;
//通过属性写入a字段
storage.a = 1;
//通过方法写入b字段
storage.setItem("b",2);
storage.getItem("a");
storage.b;
storage.clear();
}
它还比cookie多了一个优点,提供了onstorage以及storage事件,可以绑定一个回调函数,使用如下:
window.onstorage = function(e){console.log(e)}
// 或者
window.addEventListener('storage', function(){ console.log(e)})
localStorage是Storage对象的实例。对Storage对象进行任何修改,都会在触发storage事件。当通过属性或者setItem()方法保存数据,或者使用delete操作符或removeItem()删除数据,或者调用clear()方法时,都会触发该事件。通过这个事件,我们可以实现页卡之间的变动监听。
实现原理:
列表页通过storage监听localStorage的数据变动
列表页代码:
<script>
window.addEventListener("storage",function(event){
console.log("newValue is"+localStorage.getItem("tid"));
console.log("oldValue is"+event.oldValue);
window.alert('数据更新!');
},false);
</script>
当详情页有数据修改时后,写入localStorage
详情页代码:
<input id="content" type="text"/>
<button id="btn">Click</button>
<script>
window.onload=function(){
var oBtn=document.getElementById("btn");
var oInput=document.getElementById("content");
oBtn.onclick=function(){
var val=oInput.value;
localStorage.setItem("tid",val);
}
}
</script>
不过,onstorage以及storage事件,针对都是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数。还有就是在对原有的数据的值进行修改时才会触发,比如原本已经有一个key为a,值为1的localStorage,再执行:localStorage.setItem('a', 1)代码,同样是不会触发监听函数的。
缺点:
- 浏览器的容量大小不统一(比cookie大很多了),并且在高版本的浏览器才支持localStorage这个属性
- 目前所有的浏览器中都会把localStorage的值类型限定为string类型,需要JSON转换。
- localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡。
- localStorage只能监听非己页面的数据变化,这一点严重影响使用。
优点:解决了cookie容量小和时效性不足的问题。
三、WebSocket
WebSocket API是下一代客户端--服务器的异步通信方法,已被W3C进行了标准化。WebSocket API最伟大之处在于服务器和客户端可以双向实时通信。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。
它的使用很简单,如下:
// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send('I am the client and I\'m listening!');
// 监听消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
socket.close()
};
WebSocket提供了send方法和onmessage事件,用来发送和接收数据。onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。
实现原理:
列表页通过onmessage监听socket服务器发送过来的消息
列表页代码:
<script>
var socket = new WebSocket('ws://localhost:8080');
socket.onopen = function(event) {
socket.onmessage = function(event) {
console.log('Client received a message', event);
};
};
</script>
当详情页有数据修改时后,通过socket连接,通知列表页更新数据。
详情页代码:
<input id="content" type="text"/>
<button id="btn">Click</button>
<script>
var socket = new WebSocket('ws://localhost:8080');
window.onload=function(){
var oBtn=document.getElementById("btn");
var oInput=document.getElementById("content");
oBtn.onclick=function(){
var val=oInput.value;
socket.onopen = function(event) {
// 发送数据类型必须是string、ArrayBuffer、Blob之一
socket.send('数据更新!');
}
}
</script>
WebSocket的语法非常简单,不过需要IE10+浏览器才支持WebSocket通信。如果你的业务需要兼容IE8,9。业界通常使用第三方库来解决这个问题,比如Socket.IO,它使用检测功能来判断是否建立WebSocket连接,或者是AJAX long-polling连接,或Flash等,可快速创建实时的应用程序。Socket.IO还提供了一个NodeJS API,它看起来非常像浏览器的API。
缺点:它需要服务端的支持才能完成任务。如果socket数据量比较大的话,会严重消耗服务器的资源。
优点:使用简单,功能灵活、强大,如果部署了WebSocket服务器,可以实现很多实时的功能
总结:
1、Cookie+setInterval
- 原理:通过设置Cookie的domain和path属性来实现多域或多页面共享Cookie;并且通过setInterval来循环监听Cookie 的值是否发生变化
- 实现:A页面数据修改时后,写入cookie;B页面通过 setInterval 定时器循环监听cookie的数据变动
2、localStorage
- 原理:localStorage 是浏览器多个标签共用的存储空间,所以可以用来实现多标签之间的通信(session是会话级的存储空间,每个标签页都是单独的)
- 实现:A页面使用 localStorage.setItem(key,value)添加(修改、删除)内容;B页面监听 storage 事件,可得到 localstorge 存储的值(回调函数),实现不同标签页之间的通信。
- 注意:onstorage以及storage事件,针对都是非当前页面对localStorage 进行修改才会触发,当前页面修改 localStorage 不会触发监听函数。
3、WebSocket
- 原理:全双工通信自然可以实现多个标签页之间的通信。H5新增协议,目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,服务器可以在任意时刻发送消息给浏览器
- 实现:A页面有数据修改时通过socket连接,发送到服务器端;B页面通过onmessage监听socket服务器发送过来的消息
WebSocket的通信过程:
// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send('I am the client and I\'m listening!');
// 监听消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
socket.close()
};