1

在开发过程中,我们难免会遇到 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 在浏览器存储形态,以百度为例:

image

image

// 存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) => {
    // 通过建立 cookie 的时间设置,将时间设置提前一天,从而强行让 cookie 失效,最后达到删除cookie 的目的
    setCookie(name, '', '-1')
}
三、基于 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 是按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建多个对象存储空间(表),一个对象存储空间可以存储多个对象数据

image

简言之,indexedDB 提供了类似数据库风格的数据存储和使用方式,类似 NoSQL,很强大,支持所有、事物处理和健壮的查询功能。当需要存储大量数据时,indexedDB 就明显的更适合了,但它的 API 也相对比较复杂,更多 indexedDB 的特性和用法请移步这里


sugar_coffee
455 声望19 粉丝