# 浏览器本地存储方案 浏览器本地存储方案可以分为三个方面,分别为`Cookie`、`Web Storage`、`IndexedDB`。 ## Cookie 由于`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`读取/写入器。 ```javascript /*\ |*| |*| :: 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 Storage `Web`存储机制最初作为`HTML5`的一部分被定义成API的形式,但又由于其本身的独特性与其他的一些原因而剥离了出来,成为独立的一个标准,`Web`存储标准的`API`包括`locaStorage`对象和`seesionStorage`对象,其出现的原因主要有人们希望有一种在`Cookie`之外存储回话数据的途径以及望有一种存储大量可以跨会话存在的数据的机制。其实在最初的`Web`存储规范中包含了两种对象的定义,`seesionStorage`和`globalStorage`这两个对象在支持这两个对象的浏览器中都是以`Windows`对象属性的形式存在的。 ### localStorage `localStorage`对象在修订过的`HTML5`规范中作为持久保存客户端数据的方案取代了我们上面所提到的`globalStorage`。相较于`Cookie`,`localStorage`提供了简单明了的`API`来进行操作,更加安全,可储存的数据量更大。也正是出于以上这些原因,`localStorage`被视为替代`Cookie`的解决方案,但还是要注意不要在`localStorage`中存储敏感信息。 通过`localStorage`存储的数据是永久性的,除非我们使用`removeItem`来删除或者用户通过设置浏览器配置来删除,负责数据会一直保留在用户的电脑上,永不过期。`localStorage`的作用域限定在文档源级别的,即同源的才能共享,同源的文档间会共享`localStorage`的数据,他们可以互相读取对方的数据,可以通过`onstorage`事件进行监听实现同源窗口间通信,当然`localStorage`的作用域同样也受浏览器的限制。 ### sessionStorage `sessionStorage`是`Web`存储机制的另一大对象,`sessionStorage`属性允许我们去访问一个 `session Storage`对象,它与`localStorage`相似,不同之处在于`localStorage`里面存储的数据没有过期时间设置,而`Session Storage`只存储当前会话页的数据,且只有当用户关闭当前会话页或浏览器时,数据才会被清除,此外从一个`Session`派生出来的页面同样能够访问到之前设置的数据,即使新派生的页面与源页面并不同源。 ### 使用 ```javascript // 储存数据 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(); ``` ## IndexedDB 虽然`Web`存储机制对于存储较少量的数据非常便捷好用,但对于存储更大量的结构化数据来说,这种方法就不太满足开发者们的需求了,`IndexedDB`就是为了应对这个需求而产生的,它是由`HTML5`所提供的一种本地存储,用于在浏览器中储存较大数据结构的`Web API`,并提供索引功能以实现高性能查找,它一般用于保存大量用户数据并要求数据之间有搜索需要的场景,当网络断开时,用户就可以做一些离线的操作。 ### 使用 一个使用`IndexedDB`处理多个同源标签页间通信的例子。 ```javascript // 页面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); ``` ```javascript // 页面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 ```