浅谈前端存储之 cookie、localStorage、sessionStorage 和 indexedDB

在开发过程中,我们难免会遇到 token 存储、 代码缓存、 图片存储等,以及其它一些可能存在的前端存储问题。

今天我们从本地存储入手,从前端的角度来了解缓存应用的场景,以及在日常开发中,我们需要缓存的地方和使用缓存带来哪些优势 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 的特性和用法请移步这里

 

 

posted @ 2020-12-14 14:50  sugar_coffee  阅读(412)  评论(0编辑  收藏  举报