浅谈前端存储之 cookie、localStorage、sessionStorage 和 indexedDB
今天我们从本地存储入手,从前端的角度来了解缓存应用的场景,以及在日常开发中,我们需要缓存的地方和使用缓存带来哪些优势 or bug
一、什么是本地存储
-
客户端数据的存储
本地存储可以使用在哪里
-
用户临时登录信息,用户页面配置,当前临时信息等
-
一些东西用户想要存起来,下次访问可以继续使用,但是服务器没必要浪费空间来存这些信息,此时就可以应用本地存储,存放在用户本地
二、H5 之前如何实现本地存储
1、先驱者 userData
-
-
只有 IE 支持
-
XML 文件
-
userData 为IE服务,单个文件的大小限制是 128kb,一个域名下总共可以保存 1024kb 的文件。微软为他提供了相应的api,但是不符合W3C规范的,而且平台支持不够广泛,这里略过不表
2、Cookie
-
-
http 请求头上会带着,安全问题
-
大小为 4k
-
主 Domain 污染
-
它比较常用的一个应用场景就是判断用户是否登录,比如登录某个网站可以看到“记住密码”,这通常就是通过在cookie中存入一段辨别用户身份的数据来实现的。针对登录过的用户,服务器端会在他登录时往 Cookie 中插入一段加密过的唯一辨识单一用户的辨识码,下次只要读取这个值就可以判断当前用户是否登录啦。
-
-
优点:兼容性最好,几乎所有的浏览器都支持
-
缺点:大小限制非常小,而且每次发送 HTTP 请求,请求头里会带着 Cookie 的信息,会带来安全问题
-
因为 cookie 会被带入 http 的请求内容中,如果大量的使用,请求包可能会越来越大,导致请求速度慢从而影响用户体验,所以 cookie 当然是能精简就精简。
cookie 可以手动设置,也可以由服务器产生,当客户端(浏览器)向服务器发送请求,服务器会反馈一些信息给客户端,这些信息的 key / value 值被浏览器作为文件保存在客户端特定的文件夹中。
cookie 在浏览器存储形态,以百度为例:
// 存cookie let setCookie = (name, value, times) = > { let date = new Date() data.setDate(data.getDate() + times) document.cookie = name + '=' + value + ';expires=' + date } // 取cookie let getCookie = (name) => { let cookies = document.cookie let cookieArr = cookies.split(';') || [] if(!cookieArr.length) return '' for(let i = 0; i < cookieArr.length; i++) { let arr = cookieArr[i].split('=') if (name == arr[0] ) { return arr[1] } } return false } // 删除 cookie let removeCookie = (name) => { setCookie(name, '', '-1') // 通过建立 cookie 的时间设置,将时间设置提前一天,从而强行让 cookie 失效,最后达到删除cookie 的目的 }
三、基于 HTML5 规范的 Web Storage
HTML5 提供了两种在客户端存储数据的新方法:
-
-
sessionStorage 会话存储
-
localStorage 本地存储
-
Web Storage 存储方式解决了 cookie 带来的一些限制:
-
-
解决4K的大小问题
-
解决请求头常带存储信息的问题
-
解决关系型存储的问题
-
跨浏览器
-
Web Storage 本地存储数据不是由服务器请求传递的,从而它可以存储大量的数据,而不影响网站的性能。
比如在客户端保存一些用户行为或数据,如果遇到一些内容特别多的表单,为了优化用户体验,我们可以把表单页面拆分成多个子页面,然后按步骤引导用户填写,这时 sessionStorage 的作用就发挥出来了。或从接口获取的一些短期内不会更新的数据,我们也可以利用Web Storage来存储。
1、sessionStorage 临时存储神器
-
-
优点:临时,关闭页面标签自动回收,不同的两个标签页面的 sessionStorage 是不共享的
-
缺点:临时,因为是临时所以不能存储持久化的东西
-
2、localstorage 永久级别的存储
-
-
优点:兼容性中等,几乎现代的浏览器都支持,没有过期时间限制,永久存储,永不失效,即只要浏览器不卸载,数据就会一直存在,除非手动删除
-
缺点:存在大小限制,IE9,IE10 不支持
- localstorage 过期时间限制代码如下:
-
set (key, val) { const curTime = new Date().getTime() localStorage.setItem(key, JSON.stringify({ data: val, time: curTime })) }, get (key) { const data = localStorage.getItem(key) const dataObj = JSON.parse(data) if (new Date().getTime() - dataObj.time < 0) { console.log('expires') } else { console.log('expir data = ' + dataObj.data) } }
3、localStorage 和 sessionStorage 比较
-
-
相同:大约 5MB(不同浏览器有差异),操作简单,类似 key / value 的存储方式;挂载在 window 对象下;
-
不同: 保存数据的生命周期不同:localStorage 里面存储的数据没有过期时间设置,在浏览器打开期间一直保持,并且重新加载或浏览器关闭再打开仍可以获取,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除;localStorage 的数据不能跨浏览器获取,sessionStorage 不能跨页面交互,仅在当前页面中有效。
-
4、属性和方法介绍:
-
-
length 获取存储数据的数量
-
除了 length 属性以及自定义属性外,其他的属性和方法都在原型中(注:obj 为 localStorage 或 sessionStorage):
-
key(index) 获取存储数据中的第 index 个键名
-
obj.setItem(key, value) || obj.key = value || obj[key] = value 存储数据 value 的键名为 key,如果键名存在,则会覆盖对应的值
-
obj.getIem(key) || obj.key || obj[key] 获取指定键名key 对应的值
-
obj.removeItem(key) 移除指定键名 key 对应的数据
-
obj.clear() 清除本地存储中的所有数据
-
-
5、都可以存储什么内容呢?
-
-
数组、json数据、图片、脚本、样式文件
-
存储图片的实现:
setImg (key) { const img = document.createElement('img') img.src = './1323_2071n.png' img.addEventListener('load', function () { // 当图片加载完成的时候触发回调函数 const imgCanvas = document.createElement('canvas') const imgContext = imgCanvas.getContext('2d') // 确保 canvas 元素的大小和图片尺寸一致 imgCanvas.width = this.width imgCanvas.height = this.height // 渲染图片到canvas中 imgContext.drawImage(this, 0, 0, this.width, this.height) // 用 data url 的形式取出 const imgAsDataURL = imgCanvas.toDataURL('image/png') // 保存在本地存储中 try { localStorage.setItem(key, imgAsDataURL) } catch (error) { console.log(error) } }) }, getImg (key) { const srcStr = localStorage.getItem(key) const imgObj = document.createElement('img') imgObj.src = srcStr document.getElementById('body').appendChild(imgObj) }
如果一些图片不经常更改,存在localstorage,用户第二次访问的时候能很快访问,但如果图片资源很大,就比较费空间了。
6、使用注意事项:
-
-
使用前要判断浏览器是否支持
移动端的浏览器,比如 IOS 如果把无痕模式打开,是不能访问 localstorage的。还有些浏览器,是可以访问localstorage 存储的对象,但是存储的时候会报错,这时可以在 localstorage set 时进行一个异常捕获,如果捕获到异常则浏览器不支持 localstorage
-
写入数据时候,需要异常处理,避免超出容量抛错
try catch 如果超出存储大小,可以使用一些算法,比如 LRU,FIFO 来处理旧数据
-
避免用它们存储系统中的敏感数据
不是什么数据都适合放在 cookie、localStorage、sessionStorage 中的,因为只要打开控制台,就可以随意修改它们的值,如果网站中的代码存在 xss 注入的风险,它们就能对你的存储数据肆意妄为。
-
过期控制:localstorage 没有过期时间限制,如果需要有过期限制,需要自己添加过期的业务处理机制
-
key的唯一性
如果有重复key,会被覆盖
-
子域名之间不能共享存储数据
-
server 端如何取到
-
四、indexedDB Database
-
一种能在浏览器中持久的存储结构化数据的数据库,并且为 web 应用提供了丰富的查询能力
-
存储结构:
indexedDB 是按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建多个对象存储空间(表),一个对象存储空间可以存储多个对象数据
简言之,indexedDB 提供了类似数据库风格的数据存储和使用方式,类似 NoSQL,很强大,支持所有、事物处理和健壮的查询功能。当需要存储大量数据时,indexedDB 就明显的更适合了,但它的 API 也相对比较复杂,更多 indexedDB 的特性和用法请移步