浏览器本地存储方案可以分为三个方面,分别为Cookie
、Web Storage
、IndexedDB
。
由于HTTP
协议是无状态的,一旦数据交换完毕,此次链接就会关闭,再次交换数据就需要重新连接,意味着服务器无法从链接上跟踪会话。假如A
与B
同时购买了一件商品,不进行会话跟踪的话服务器就无法判断究竟是谁购买了此商品。服务端为进行会话跟踪,给每个客户端颁发一个通行证,每个人访问必须携带通行证,这样服务端就能区别用户身份了。
Cookie
实际上是一小段的文本信息,服务端将需要通行证信息Cookie
发送到浏览器,浏览器将通行证存储起来,并且对于同源的每个请求都会自动携带通行证信息(CSRF
跨站请求伪造基于此策略),于是服务端就可以判断用户身份。
Cookie
通常用于存储一些通用的数据,比如用户的登陆状态、首选项等,而不建议存储业务数据,虽然随着时代的进步,HTML5
所提供的Web
存储机制已经逐步替代了Cookie
,但有些较为老的浏览器还是不兼容Web
存储机制,所以在某些需求下为了处理兼容性的情况可能还是需要Cookie
存储一些业务信息。
Cookie
的兼容性非常的好,兼容现在市面上所有的主流浏览器。
- 存储量小,虽不同浏览器的存储量不同,但基本上都是在
4KB
左右。 - 影响性能,由于
Cookie
会由浏览器作为请求头发送,因此当Cookie
存储信息过多时,会影响特定域的资源获取的效率,增加文档传输的负载。 - 安全问题,存储在
Cookie
的任何数据可以被访问,因此不能在Cookie
中储存敏感信息,此外重要的Cookie
还需要使用HTTP ONLY
防止恶意的Js
读写。 - 由于第三方
Cookie
的滥用,有些用户在浏览网页时会禁用Cookie
,所以我们不得不测试用户是否支持Cookie
。
一个完整支持Unicode
的Cookie
读取/写入器。
/*\
|*|
|*| :: cookies.js ::
|*|
|*| A complete cookies reader/writer framework with full unicode support.
|*|
|*| https://developer.mozilla.org/en-US/docs/DOM/document.cookie
|*|
|*| This framework is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*| Syntaxes:
|*|
|*| * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
|*| * docCookies.getItem(name)
|*| * docCookies.removeItem(name[, path], domain)
|*| * docCookies.hasItem(name)
|*| * docCookies.keys()
|*|
\*/
var docCookies = {
getItem: function (sKey) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
},
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
var sExpires = "";
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toUTCString();
break;
}
}
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
return true;
},
removeItem: function (sKey, sPath, sDomain) {
if (!sKey || !this.hasItem(sKey)) { return false; }
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : "");
return true;
},
hasItem: function (sKey) {
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
},
keys: /* optional method: you can safely remove it! */ function () {
var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
for (var nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); }
return aKeys;
}
};
Web
存储机制最初作为HTML5
的一部分被定义成API的形式,但又由于其本身的独特性与其他的一些原因而剥离了出来,成为独立的一个标准,Web
存储标准的API
包括locaStorage
对象和seesionStorage
对象,其出现的原因主要有人们希望有一种在Cookie
之外存储回话数据的途径以及望有一种存储大量可以跨会话存在的数据的机制。其实在最初的Web
存储规范中包含了两种对象的定义,seesionStorage
和globalStorage
这两个对象在支持这两个对象的浏览器中都是以Windows
对象属性的形式存在的。
localStorage
对象在修订过的HTML5
规范中作为持久保存客户端数据的方案取代了我们上面所提到的globalStorage
。相较于Cookie
,localStorage
提供了简单明了的API
来进行操作,更加安全,可储存的数据量更大。也正是出于以上这些原因,localStorage
被视为替代Cookie
的解决方案,但还是要注意不要在localStorage
中存储敏感信息。
通过localStorage
存储的数据是永久性的,除非我们使用removeItem
来删除或者用户通过设置浏览器配置来删除,负责数据会一直保留在用户的电脑上,永不过期。localStorage
的作用域限定在文档源级别的,即同源的才能共享,同源的文档间会共享localStorage
的数据,他们可以互相读取对方的数据,可以通过onstorage
事件进行监听实现同源窗口间通信,当然localStorage
的作用域同样也受浏览器的限制。
sessionStorage
是Web
存储机制的另一大对象,sessionStorage
属性允许我们去访问一个 session Storage
对象,它与localStorage
相似,不同之处在于localStorage
里面存储的数据没有过期时间设置,而Session Storage
只存储当前会话页的数据,且只有当用户关闭当前会话页或浏览器时,数据才会被清除,此外从一个Session
派生出来的页面同样能够访问到之前设置的数据,即使新派生的页面与源页面并不同源。
// 储存数据
localStorage.setItem("key", "value");
sessionStorage.setItem("key", "value");
/**
* 由于存储数据会调用 toString() 方法
* Object 类型会存储为 [object Object] 字符串
* 所以进行存储时需调用 JSON.stringify() 转化为字符串
* 取出时调用 JSON.parse() 将字符串转回对象
*/
// 读取数据
localStorage.getItem("key");
sessionStorage.getItem("key");
// 删除数据
localStorage.removeItem("key");
sessionStorage.removeItem("key");
// 清空数据
localStorage.clear();
sessionStorage.clear();
虽然Web
存储机制对于存储较少量的数据非常便捷好用,但对于存储更大量的结构化数据来说,这种方法就不太满足开发者们的需求了,IndexedDB
就是为了应对这个需求而产生的,它是由HTML5
所提供的一种本地存储,用于在浏览器中储存较大数据结构的Web API
,并提供索引功能以实现高性能查找,它一般用于保存大量用户数据并要求数据之间有搜索需要的场景,当网络断开时,用户就可以做一些离线的操作。
一个使用IndexedDB
处理多个同源标签页间通信的例子。
// 页面A
var db = null;
var request = indexedDB.open("message");
request.onsuccess = (e) => db = e.target.result;
request.onupgradeneeded = function(event) {
db = event.target.result;
if (!db.objectStoreNames.contains('message')) {
db.createObjectStore('message', { keyPath: 'key' });
}
};
function setData(data){
var transaction = db.transaction(['message'], 'readwrite');
var store = transaction.objectStore(['message']);
var requestData = store.put({ key: "msg", info: data});
requestData.onsuccess = function(e) {
console.log(e.target.result);
};
};
setTimeout(() => setData(1),1000);
// 页面B
var db = null;
var request = indexedDB.open("message");
request.onsuccess = (e) => db = e.target.result;
function readMsg(){
var transaction = db.transaction(['message']);
var objectStore = transaction.objectStore('message');
var requestResult = objectStore.get('msg');
requestResult.onsuccess = function(event) {
console.log(requestResult.result.info);
};
}
setTimeout(readMsg, 3000);
https://github.com/WindrunnerMax/EveryDay
https://zhuanlan.zhihu.com/p/146050407
https://juejin.cn/post/6844904193694646280
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage
https://www.cnblogs.com/fengyuqing/archive/2013/05/31/localStorage.html
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB