前端持久化存储漫谈

166 阅读6分钟

本文正在参加「技术专题19期 漫谈数据库技术」活动

前端持久化缓存的目的主要是为了优化用户体验,减少接口调用频次,提升首屏渲染速度等。

web1.0时代

因为UGC的来源通常是各类网站的运营者,互联网用户更多时候是作为消费者的角色出现,彼时,用户与页面间的交互比较少,要求比较低,通常来说,web1.0时代,页面打得开,没有图裂,就算是很好的用户体验了。

前web2.0时代

时代的车轮滚滚向前,随着互联网基础设施的飞速发展,互联网用户渐渐不满足于仅仅浏览内容,大胆发言,在互联网浪潮中肆意奔驰,逐渐成为主流,校园网、人人网、天涯、猫扑等论坛如雨后春笋般应运而生,此时,用户已经开始逐渐成为内容的生产者,开始参与到互联网的各类“厮杀”之中。 此时,对于用户来说,哪里人多,哪里内容更丰富,哪里可以畅所欲言,页面不卡顿,哪里就是天堂

后web2.0时代

时代的车轮继续滚滚向前,有句话说当时代抛弃你的时候,连招呼都不会打一声,随着经济进一步飞速发展,人们对于互联网平台的要求也进一步提高,QQ、微信、微博、知乎、小红书、豆瓣、B站、掘金。。。 各类平台开始出现内容的细分,不同平台也吸引着不同的用户,此时,人们越来越开始关注用户体验,诸如 清爽的界面设计、容易上手的界面交互、优秀的运营团队、顺畅的反馈渠道等等越来越成为人们长期驻留某平台的理由。

得益于标准制定组织和广大研发工程师的努力,ECMA作为事实上的标准被确立下来,也为前端实现更优秀的用户体验打下了坚实的基础。

前端持久化存储主要依托的技术按照时间线来排序,主要有以下几个

  • cookie 设计之初,是为了解决无状态下的用户会话保持的问题,通常会存储诸如用户信息是session,服务器交互所需的必要信息等,cookie主要包含下面的信息
  1. 内容 document.cookie="username=avion";
  2. 过期时间,默认情况下,浏览器会在关闭时清除本站点的cookie document.cookie="username=avion; expires=Thu, 18 Dec 2043 12:00:00 GMT"
  3. 生效路径 默认情况下,cookie仅在当前路径下生效 document.cookie="username=avion; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/
  4. 是否仅服务器可读 这个表示通过document.cookie获取时,无法获取到仅服务器可读的cookie,这样可以有效的避免XSS攻击

设置cookie

function setCookie (cname, cvalue, exdays) {
  const d = new Date()
  d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000))
  const expires = 'expires=' + d.toGMTString()
  document.cookie = cname + '=' + cvalue + '; ' + expires
}

获取cookie

function getCookie (cname) {
  const name = cname + '='
  const ca = document.cookie.split(';')
  for (let i = 0; i < ca.length; i++) {
    const c = ca[i].trim()
    if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
  }
  return ''
}

监测cookie

function checkCookie () {
  let username = getCookie('username')
  if (username != '') {
    alert('Welcome again ' + username)
  } else {
    username = prompt('Please enter your name:', '')
    if (username != '' && username != null) {
      setCookie('username', username, 365)
    }
  }
}
  • sessionStorage与localStorage 相比较cookie较短的存储长度,以及仅仅作为客户端标识的象征性意义,sessionStorage与localStorage才真正开了前端持久化存储的先河,通过以上的技术,我们可以很容易实现菜单缓存,特定信息缓存,状态跨页签保持等等,真正意义上来讲,sessionStorage与localStorage开启了前端持久化存储的先河,也是前端数据库webSQLindexDB的前身
  1. localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除。
  2. sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。

不管是localStorage还是sessionStorage,他们都具有相同的api,所以我们用表格体现即可,api的命名也是顾名思义非常形象,不用看描述就能知道api 的基本用法

方法描述
key(n)返回存储对象中第 n 个键的名称
getItem(keyname)返回指定键的值
setItem(keynamevalue)添加键和值,如果对应的值存在,则更新该键对应的值。
removeItem(keyname)移除键
clear()清除存储对象中所有的键

接下来,就是我们今天的重头戏了,让我们隆重请出今天的特邀嘉宾localforage!!!

有些人可能就要好奇了,为什么是localforage而不是webSQLindexDB呢?因为我们最终的最终,使用的还是这个前端数据库管理库localforage,让我们先隆重介绍这个重要嘉宾,然后再回头去看webSQLindexDB

localforage的价值

localForage 是一个 JavaScript 库,通过简单类似 localStorage API 的异步存储来改进你的 Web 应用程序的离线体验。它能存储多种类型的数据,而不仅仅是字符串。

localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。

localforage的安装

cdn : 最新版本

npm : npm install localforage 我们只需要对文件进行引用即可

localforage的使用

localforage的api就像他价值里说的类似localstorage的API,

获取值的回调函数形式

getItem(key, successCallback)

从仓库中获取 key 对应的值并将结果提供给回调函数。如果 key 不存在,getItem() 将返回 null

获取值的Promise的形式

getItem(key).then(calback)

从仓库中获取 key 对应的值并通过thenable的形式反馈给回调函数。如果 key 不存在,getItem() 将返回 null

设置值的回调函数形式 setItem(key, value, successCallback)

将数据保存到离线仓库。你可以存储如下类型的 JavaScript 对象:

  • Array
  • ArrayBuffer
  • Blob
  • Float32Array
  • Float64Array
  • Int8Array
  • Int16Array
  • Int32Array
  • Number
  • Object
  • Uint8Array
  • Uint8ClampedArray
  • Uint16Array
  • Uint32Array
  • String

可以看出来,localforage在存储对象时,不需要进行JSON.stringify序列化,这样无疑是很方便的

移除值的回调函数形式

removeItem(key, successCallback)

从离线仓库中删除 key 对应的值。

清空数据库的所有值 clear(successCallback)

从数据库中删除所有的 key,重置数据库。

localforage的优雅降级与主动设置驱动

默认情况下,localForage 按照以下顺序选择数据仓库的驱动:

  1. IndexedDB
  2. WebSQL
  3. localStorage

如果你想强制使用特定的驱动,可以使用 setDriver(),参数为以下的某一个或多个:

  • localforage.INDEXEDDB
  • localforage.WEBSQL
  • localforage.LOCALSTORAGE setDriver(driverName)
    setDriver([driverName, nextDriverName])

若可用,强制设置特定的驱动。

说完主角,我们接下来再来看看为什么我们不直接使用webSql或者indexDB

不论是webSql或者indexDB,他们都不是现有的HTML5规范的一部分,而是事实上的独立规范,作为前端数据库实现规范存在,websql的语法类似于SQL,核心方法是

  1. openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。
  2. transaction:这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚。
  3. executeSql:这个方法用于执行实际的 SQL 查询。

因为前端很少接触SQL相关的内容,SQL的语法也带来额外的学习成本,以及各浏览器对标准实现的进度与实现形式的差异,选择webSQL就意味着我们面临着浏览器兼容适配与SQL语法学习两重大山,特别是对于webSQL来讲,SQL语法哪些适用,哪些不适用,都属于黑盒,面临大量的测试。

indexDBwebSQL类似,也是类SQL的语法,当然也会面临上面我们所发现的问题

一锤定音

所以,我们今天的主角localforage 应运而生, 不管从优雅降级的角度考虑,还是学习和应用维护成本角度考虑,localforage都是我们的最佳技术选型,通过底层对API的适配与封装,降低了我们的学习成本和上手难度,解决了实际生产应用所面临的种种问题,是离线应用以及渐进式增强的web框架所必备的依赖库,通过localforage我们可以很容易的实现界面模版在前端的存储与设计,降低多次接口交互带来的渲染成本,提升用户体验。