浏览器存储
在浏览器端存储数据对我们是很有用, 这相当于赋予浏览器记忆的功能, 可以纪录用户的所有状态信息, 增强用户体验. 比如当纪录用户的登陆状态时, 可以让用户能够更快的进行访问, 而不是每次登陆时都需要去进行繁琐的操作.
总的来说, 现在市面上最常见的数据存储方案是以下三种:
Cookie
web 存储 (localStorage 和 seesionStorage)
IndexedDB
Cookie
Cookie 的又称是 HTTP Cookie, 最初是在客户端用于存储会话信息, 从底层来看, 它作为 HTTP 协议的一种扩展实现, Cookie 数据会自动在 Web 浏览器和 Web 服务器之间传输, 因此在服务器端脚本就可以读写存储的 cookie 的值, 因此 Cookie 通常用于存储一些通用的数据, 比如用户的登陆状态, 首选项等. 虽然随着时代的进步, html5 所提供的 Web 存储机制已经逐步替代了 Cookie, 但有些较为老的浏览器还是不兼容 Web 存储机制, 因此正处于这个老旧更替阶段的我们对于它还是要了解了解的.
Cookie 的优点
首先由于操作 Cookie 的 API 很早就已经定义和实现了, 因此相比于其他的数据存储方式, Cookie 的兼容性非常的好, 兼容现在市面上所有的主流浏览器, 我们在使用它的时候完全不用担心兼容问题.
Cookie 的缺点
说到 Cookie 的缺点, 那就有点多了, 不然也不会在 Cookie 后面出现 Web 存储等新的数据存储的方案了.
总结起来 Cookie 的缺点主要是以下几点:
存储量小. 虽不同浏览器的存储量不同, 但基本上都是在 4kb 左右.
影响性能. 由于 Cookie 会由浏览器作为请求头发送, 因此当 Cookie 存储信息过多时, 会影响特定域的资源获取的效率, 增加文档传输的负载.
只能储存字符串.
安全问题. 存储在 Cookie 的任何数据可以被他人访问, 因此不能在 Cookie 中储存重要的信息.
由于第三方 Cookie 的滥用, 所以很多老司机在浏览网页时会禁用 Cookie, 所以我们不得不测试用户是否支持 Cookie, 这也是很麻烦的一件事.
Cookie 的操作
基本的 Cookie 操作主要有三个: 读取, 写入和删除. 但在 JavaScript 中去处理 cookie 是一件很繁琐的事情, 因为 cookie 中的所有的名字和值都是经过 URI 编码的, 所以当我们必须使用 decodeURICompoent 来进行解码才能得到 cookie 的值. 我们来看看 CookieUtil 对象是如何操纵 cookie 的:
- var CookieUtil = {
- // get 可根据 cookie 的名字获取相应的值
- get: function() {
- const cookieName = encodeURIcOMPONET(name) + "=",
- cookieStart = document.cookie.indexOf(cookieName),
- cookieValue = null
- if(cookieStart> -1) {
- const cookieEnd = document.cookie.indexOf(";", cookieStart)
- if(cookieEnd == -1) {
- cookieEnd = document.cookie.length
- }
- cookieValue = decodeURICompoent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd))
- }
- return cookieValue
- }
- // set 设置一个 cookie
- set: function(name, value, expires, path, domain, secure) {
- var cookieText = encodeURIComponet(name)+"="+encodeURIComponet(value)
- if(expires instanceof Date) {
- cookieText += "; expires=" + expires.toGMTString()
- }
- if(path) {
- cookieText += ";path=" + path
- }
- if(domain) {
- cookieText += "; domain" + domain
- }
- if(secure) {
- cookieText += "; secure"
- }
- document.cookie = cookieText
- }
- // 删除已有的 cookie
- unset: function(name, path, domain, secure) {
- this.set(name, "", new Date(0), path, domain, secure)
- }
- }
是不是很麻烦, 无论是获取一个 cookie 的值或是设置一个 cookie 都是很麻烦的事情, 这也成为了后续的浏览器数据存储方案出现的一大原因.
Web 存储
Web 存储机制最初作为 HTML5 的一部分被定义成 API 的形式, 但又由于其本身的独特性与其他的一些原因而剥离了出来, 成为独立的一个标准. Web 存储标准的 API 包括 locaStorage 对象和 seesionStorage 对象. 它所产生的主要原因主要出于以下两个原因:
人们希望有一种在 cookie 之外存储回话数据的途径.
人们希望有一种存储大量可以跨会话存在的数据的机制.
- (注: 其实在最初的 Web 存储规范中包含了两种对象的定义: seesionStorage 和 globalStorage, 这两个对象在支持这两个对象的浏览器中都是以 Windows 对象属性的形式存在的)
- localStorage
localStorage 对象在修订过的 HTML5 规范中作为持久保存客户端数据的方案取代了我们上面所提到的 globalStorage. 从功能上来讲, 我们可以通过 locaStorage 在浏览器端存储键值对数据, 它相比于 cookie 而言, 提供了更为直观的 API, 且在安全上相对好一点
, 而且虽然 localStorage 只能存储字符串, 但它也可以存储字符串化的 JSON 数据, 因此相比于 cookie,localStorage 能存储更复杂的数据. 总的来说相较于 cookie,localStorage 有以下优势:
提供了简单明了的 API 来进行操作
更加安全
可储存的数据量更大
也正是出于以上这些原因, localStorage 被视为替代 cookie 的解决方案, 但还是要注意不要在 localStorage 中存储敏感信息.
localStorage 的基本语法
localStorage 的基本操作很简单, 示例如下:
- // 使用方法存储数据
- localStorage.setItem("name", "Srtian")
- // 使用属性存储数据
- localStorage.say = "Hello world"
- // 使用方法读取数据
- const name = localStorage.getItem("name")
- // 使用属性读取数据
- const say = localStorage.say
- // 删除数据
- localStorage.removeItem("name")
但需要注意的是, 我们上面的示例全是存储字符串格式的数据, 当我们需要传输其他格式的数据时, 我们就需要将这些数据全部转换为字符串格式, 然后再进行存储:
- const user = {
- name:"Srtian", age: 22
- }
- localStorage.setItem("user", JSON.stringify(user))
当然, 我们在获取值的时候也别忘了将其转化回来:
var age = JSON.parse(localStorage.user)
localStorage 储存数据的有效期与作用域
通过 localStorage 存储的数据时永久性的, 除非我们使用 removeItem 来删除或者用户通过设置浏览器配置来删除, 负责数据会一直保留在用户的电脑上, 永不过期.
localStorage 的作用域限定在文档源级别的(意思就是同源的才能共享), 同源的文档间会共享 localStorage 的数据, 他们可以互相读取对方的数据, 甚至有时会覆盖对方的数据. 当然, localStorage 的作用域同样也受浏览器的限制.
localStorage 的兼容
localStorage 的兼容如下表所示:
- Feature Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
- localStorage 4 (Yes) 3.5 8 10.50 4
- sessionStorage 5 (Yes) 2 8 10.50 4
sessionStorage
sessionStorage 是 Web 存储机制的另一大对象, sessionStorage 属性允许我们去访问一个 session Storage 对象. 它与 localStorage 相似, 不同之处在于 localStorage 里面存储的数据没有过期时间设置, 而 Session Storage 只存储当前会话页的数据, 且只有当用户关闭当前会话页或浏览器时, 数据才会被清除.
sessionStorage 的基本语法
我们可以通过下面的语法, 来保存, 获取, 删除数据, 大体语法与:
- // 保存数据到 sessionStorage
- sessionStorage.setItem('name', 'Srtian');
- // 从 sessionStorage 获取数据
- var data = sessionStorage.getItem('name');
- // 从 sessionStorage 删除保存的数据
- sessionStorage.removeItem('name');
- // 从 sessionStorage 删除所有保存的数据
- sessionStorage.clear();
下面的示例会自动保存一个文本输入框的内容, 如果浏览器因偶然因素被刷新了, 文本输入框里面的内容会被恢复, 写入的内容不会丢失:
- // 获取文本输入框
- var field = document.getElementById("field")
- // 检测是否存在 autosave 键值
- // (这个会在页面偶然被刷新的情况下存在)
- if (sessionStorage.getItem("autosave")) {
- // 恢复文本输入框的内容
- field.value = sessionStorage.getItem("autosave")
- }
- // 监听文本输入框的 change 事件
- field.addEventListener("change", function() {
- // 保存结果到 sessionStorage 对象中
- sessionStorage.setItem("autosave", field.value)
- })
在兼容性和优点方面, sessionStorage 和 localStorage 是差不多的, 因此在此也就不多说了, 下面我们来聊一聊 IndexedDB.
IndexedDB
虽然 Web 存储机制对于存储较少量的数据非常便捷好用, 但对于存储更大量的结构化数据来说, 这种方法就不太满足开发者们的需求了. IndexedDB 就是为了应对这个需求而产生的, 它是由 HTML5 所提供的一种本地存储, 用于在浏览器中储存较大数据结构的 Web API, 并提供索引功能以实现高性能查找. 它一般用于保存大量用户数据并要求数据之间有搜索需要的场景, 当网络断开时, 用户就可以做一些离线的操作. 它较之 SQL 更为方便, 不需要写一些特定的语法对数据进行操作, 数据格式是 JSON.
IndexedDB 的基本语法
使用 IndexedDB 在浏览器端存储数据会比上述的其他方法更为复杂. 首先, 我们需要创建数据库, 并指定这个数据库的版本号:
- // 注意数据库的版本号只能是整数
- const request = IndexedDB.open(databasename, version)
然后我们需要生成处理函数, 需要注意的是 onupgradeneeded 是我们唯一可以修改数据库结构的地方. 在这里面, 我们可以创建和删除对象存储空间以及构建和删除索引.
- :
- request.onerror = function() {
- // 创建数据库失败时的回调函数
- }
- request.onsuccess = function() {
- // 创建数据库成功时的回调函数
- }
- request.onupgradeneededd = function(e) {
- // 当数据库改变时的回调函数
- }
然后我们就可以建立对象存储空间了, 对象存储空间仅调用 createObjectStore()就可以创建. 这个方法使用存储空间的名称, 和一个对象参数. 即便这个参数对象是可选的, 它还是非常重要的, 因为它可以让我们定义重要的可选属性和完善你希望创建的对象存储空间的类型.
- request.onupgradeneeded = function(event) {
- const db = event.target.result
- const objectStore = db.createObjectStore('name', { keyPath:'id' })
- }
对象的存储空间我们已经建立好了, 接下来我们就可以进行一系列的骚操作了, 比如来个蛇皮走位! 不不不, 口误口误, 比如添加数据:
- addData: function(db, storename, data) {
- const store = store = db.transaction(storename, 'readwrite').objectStore(storename)
- for(let i = 0; i <data.length; i++) {
- const request = store.add(data[i])
- request.onerror = function() {
- console.error('添加数据失败')
- }
- request.onsuccess = function() {
- console.log('添加数据成功')
- }
- }
- }
如果我们想要修改数据, 语法与添加数据差不多, 因为重复添加已存在的数据会更新原本的数据, 但还是有细小的差别:
- putData: function(db, storename, data) {
- const store = store = db.transaction(storename, 'readwrite').objectStore(storename)
- for(let i = 0; i < data.length; i++) {
- const request = store.put(data[i])
- request.onerror = function() {
- console.error('修改数据失败')
- }
- request.onsuccess = function() {
- console.log('修改数据成功')
- }
- }
- }
获取数据:
- getDataByKey: function(db, storename, key) {
- const store = store = db.transaction(storename, 'readwrite').objectStore(storename)
- const request = store.get(key)
- request.onerror = function() {
- console.error('获取数据失败')
- }
- request.onsuccess = function(e) {
- const result = e.target.result
- console.log(result)
- }
- }
删除数据:
- deleteDate: function(db, storename, key) {
- const store = store = db.transaction(storename, 'readwrite').objectStore(storename)
- store.delete(key)
- console.log('已删除存储空间' + storename + '中的' + key + '纪录')
- }
关闭数据库:
db.close
IndexedDB 的优点(相较于前面的存储方案)
拥有更大的储存空间
能够处理更为复杂和结构化的数据
拥有更多的交互控制
每个'database'中可以拥有多个'database'和'table'
IndexedDB 的局限性
了解了 IndexedDB 的优点, 我们当然也要来聊一聊 IndexedDB 的局限性与适用的场景:
1. 存储空间限制
一个单独的数据库项目的大小没有限制. 然而可能会限制每个 IndexedDB 数据库的大小. 这个限制 (以及用户界面对它进行断言的方式) 在各个浏览器上也可能有所不同:
Firefox: 对 IndexedDB 数据库的大小没有限制. 在用户界面上只会针对存储超过 50 MB 大小的 BLOB(二进制大对象)请求权限. 这个大小的限额可以通过 dom.indexedDB.warningQuota 首选项进行自定义.(定义在 ).
Google Chrome:
2. 兼容性问题
图片. PNG
从上面的图我们可以看出对于 IndexedDB 的兼容来讲比前面所提及的存储方案要差不少, 因此在使用 IndexedDB 时, 我们也要好好的考虑兼容性的问题
3. indexedDB 受同源策略的限制
indexedDB 使用同源原则, 这意味着它把存储空间绑定到了创建它的站点的源(典型情况下, 就是站点的域或是子域), 所以它不能被任何其他源访问. 要着重指出的一点是 IndexedDB 不适用于从另一个站点加载进框架的内容 (不管是 <frame> 还是 <iframe>. 这是一项安全措施. 详情请看这个:
除此之外, IndexedDB 还存在诸如: 不适合存储敏感数据, 相较于 Web 存储机制的操作更加复杂等问题, 这都是我们在使用 IndexedDB 时需要考虑的.
来源: https://juejin.im/post/5c93aca1f265da60df4107a9