存储引擎及如何选择合适的存储 API

原文请查阅 这里,略有删减,本文采用 知识共享署名 4.0 国际许可协议共享,BY Troland

本系列持续更新中,Github 地址请查阅这里javascript

这是 JavaScript 工做原理的第十六章。java

概述

每当设计网页程序的时候,为本地设备选择合适的存储机制尤其重要。一个好的存储引擎能够帮助开发人员有效地存储数据,减小传输带宽及提升程序的响应速度。正确的存储缓存策略是构建移动端离线网页体验的核心组成部分,愈来愈多的用户想固然觉得能够离线使用移动端网页程序。git

本章,咱们将讨论各类可用的存储 API 和服务,并提供一些在构建网页程序时如何正确地选择存储引擎的建议。github

数据模型

数据存储模型决定了其内部是如何组织存储数据的。这会影响到整个网页程序的设计,并计算出为获取高性能网页程序及解决其所遇到的问题所须要的代价。没有所谓更好的技术和一刀切的解决方案,由于全部的问题都是工程学相关的问题。那么,让咱们来瞧瞧可供选择的数据模型吧:web

  • 结构型:以预约义字段将数据存储于表中,因其为典型的基于 SQL 的数据库管理系统,因此能够很好地适应灵活和动态的数据查询。IndexedDB 即浏览器端结构型数据库的一个典型例子。
  • 键/值型:键/值数据存储及关系型 NoSQL 数据库,容许开发者经过惟一键索引来存储和获取非结构型数据(即非预约数据类型的字段的数据)。键/值数据存储就像哈希表存储,意及其容许在必定时间内访问索引的不定数据类型的数据。键/值数据型存储的很好的例子有浏览器端的 Cache API 和 服务器端 Apache Cassandra。
  • 字节流型:这一简单的模型把数据存储为定长,混淆字符串的字节变量,让应用层来控制其内部数据组织。该模型尤为适合于文件存储和其它层次型组织的 blob 数据。字节流存储的典型例子包括文件系统和云存储设备。

持久性

网页程序的数据存储方法能够以数据的存储时长来进行分析:数据库

  • 会话持久性:仅当活动的单个网页会话或者浏览器选项卡时数据有效,关闭即失效。会话持久性数据存储一个例子即 Session Storage API
  • 设备持久性:该类数据存储于指定设备的跨会话和浏览器选项卡/窗口有效。设备持久性存储机制的一个例子即 Cache API
  • 全局持久性:该类数据跨会话和设备存储。所以,它是兼容性最好的数据持久性方案。它不会存储于设备上,这意味着须要从服务端存储中得到数据。由于这里只讨论针对设备的数据存储,因此这里只是稍微提下服务端数据存储。

客户端数据持久性

现现在,有至关多的浏览器 API 可供选择用于存储数据。这里将详细讨论这些方法,而后对其进行比较以便让开发者轻松地选择正确的数据存储方案。api

然而,首先在选择如何存储数据以前,开发者须要考虑几件事情 。固然了,第一件事即必须想清楚打算如何使用网页程序及以后的维护和性能优化。即便成竹在胸,可代选择的方案可能只有几个。如下为开发者须要考虑的问题:跨域

  • 浏览器支持-优先考虑标准化和组织良好的 API,由于这些 API 不会轻易变更且兼容性好。这些 API 一样有很是丰富的文档和活跃的开发者社区。
  • 事务-有时候,事务对于相关的数据存储操做集原子化成功或失败相当重要。传统数据库使用事务模型来实现该功能,在事务模型中以把相关数据更新划分为任意的单元。
  • 同步/异步-少数存储 API 是同步的意即存储或者检索数据请求会阻塞当前活跃线程直到数据请求结束。使用同步数据存储 API 会阻塞主线程且会让程序界面假死。尽可能使用异步存储 API。

对比

这里,让咱们浏览一下网页开发者当前可用的 API 并使用上述的几个维度来进行比较。浏览器

<iframe src="https://airtable.com/embed/sh...;></iframe>缓存

文件系统 API

0_9kpehy4mub8f-hsp

有了 FileSystem API,网页程序就能够在用户本地文件系统的沙箱中进行新建,读取,操做和写文件。

该接口包含以下几个部分:

  • 读取和操做文件:File/BlobFileListFileReader
  • 新建和写文件:Blob()FileWriter
  • 访问目录和文件系统:DirectoryReaderFileEntry/DirectoryEntryLocalFileSystem

文件系统 API 并非标准的 API.因其兼容性不太好,因此切记不要在生产环境中使用。各类浏览器厂商的实现会有很大的不一样且该 API 之后可能会变动。

文件和目录条目 API 接口文件系统用来表示一个文件系统。可从任意文件系统条目的 filesystem 属性中获取这些 对象。少数浏览器提供了额外的 API 来建立和操做文件系统。

该接口不会容许开发者访问用户的文件系统。相反,开发者会在浏览器沙箱内得到一个虚拟磁盘。若想要访问用户的文件系统,能够采起安装 Chrome 插件的方法。

得到文件系统

网页程序可调用 window.requestFileSystem() 来访问沙箱文件系统。:

// 注意: 该文件系统以 Google Chrome 12 为前缀The file system has been prefixed as of Google Chrome 12:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(type, size, successCallback, opt_errorCallback)

第一次调用 requestFileSystem() 方法的时候会新建一个本地存储。须要注意的是该文件系统是沙箱型的,意即网页程序不能够访问其它程序的文件。

在得到访问文件系统的权限后,开发者能够对文件和目录进行大部分常规文件系统操做。

和其它存储策略相比,FileSystem 大有不一样,它旨在知足数据库不能很好地解决客户端存储的状况。通常来讲,程序用来处理大型二进制 blobs 文件或者和浏览器上下文以外的程序分享数据。

如下为使用 FileSystem 的好范例:

  • 断点续传工具-当选择文件或者文件目录来上传的时候,会把文件复制进沙箱后一次上传一个分片。
  • 视频游戏,音乐或者其它含有大量媒体文件的程序
  • 为了更好的性能提供离线访问或者本地缓存功能的音频/图片编辑器-数据 blobs 通常会很是庞大且须要可读写
  • 离线视频播放器-其须要下载大型文件以备以后观看或者快速的寻轨-缓冲
  • 离线网页邮件客户端-下载附件且进行本地存储

如下为该 API 的当前浏览器支持状况:

0_ndu4n8xqf6qeqmsy

Local storage

0_asohzlowolitnuel

localStorage API 容许开发者访问 文档 源的 Storage 对象。存储的数据在多个浏览器会话之间仍然有效。localStorage 和 sessionStorage 相似,只不过存储在 localStorage 中的数据没有过时时间,而存储在 sessionStorage 中的数据会在页面会话结束时丢失-意即当关闭页面时即丢失。

不管是 localStorage 仍是 sessionStorage 其数据只存储在特定的页面源中,所谓页面源包含协议,主机名和端口。

如下为该 API 的当前浏览器支持状况:

0_hxc_nupnycubhj-l

Session storage

0_-imsnws_l1g0syla

sessionStorage 属性容许开发者访问当前源的会话 Storage 对象。前面简述过,sessionStorage 和 localStorage 相似。惟一的区别即,存储在 localStorage 中的数据没有过时时间而 sessionStorage 中的数据会在页面会话结束时丢失。页面会话的时效为浏览器打开时且在页面重载和恢复时。在新的选项卡中打开新页面或者窗口会致使从新初始化一个新的会话,这与会话 cookies 的工做机制是不同的。

请注意不管数据存储于 sessionStorage 仍是 localStorage 都仅只在指定的页面源中有效。

如下为该 API 的当前浏览器支持状况:

0_hxc_nupnycubhj-l

Cookies

0_vkqiniyfu2o7d7bh

所谓 cookie(网页 cookie,浏览器 cookie) 指的是由用户的服务器发送到客户端的一小段数据。浏览器将其存储下来而后在下一次请求的时候捎带上它发往服务器。典型地,它被用来告知两个请求是否来自于同一个客户端-好比保持用户登陆状态。它为 无状态 HTTP 协议记录有状态信息。

Cookies 有如下三个主要用途:

  • 会话管理-登陆,购物车,游戏积分或者其它须要在服务端存储的数据。
  • 个性化-用户参数,皮肤和其它设置
  • 监控-记录和分析用户行为

Cookies 曾经一统客户端存储方案。当它是客户端存储的惟一方案的时候,这是不二选择,现现在推荐选择使用现代存储 API 来存储客户端数据。每次发送请求都会捎带上 Cookies,因此会影响性能(特别是当在一个移动端请求数据的时候)。

有两种类型的 cookies:

  • 会话 cookie-当用户关闭浏览器时失效。网页浏览器可使用恢复会话技术来固化大多数会话 cookie,就好像不曾关闭浏览器同样。
  • 永久性 cookie-和客户端关闭即过时相反,永久性 cookie 会在指定的过时时间过时或者在一个指定的时间(Max-age)后过时。

请注意不要在 cookie 中存储凭据或者敏感信息,因其固有的不安全缺陷机制。

然而,不肖说,cookie 是兼容性最好的方案。

Cache

0_xz2u-ztabhwjosky

Cache 接口是缓存请求/响应对象的存储机制。请注意和 workers 同样可在窗口做用域内使用 Cache 接口。虽然 Cache 是在服务工做线程规范中定义的,但这并不表示必定要和服务工做线程一块儿使用。

一个源能够拥有多个命名的缓存对象。开发者只须要在脚本(好比在服务工做线程中)中实现如何处理更新缓存便可。除非显示请求不然不会更新缓存中的对象,只能经过删除缓存对象,不然不会过时。使用 CacheStorage.open() 来打开指定命名的缓存对象,而后调用任意的缓存方法来维护缓存。

开发者须要定时清除缓存条目。每一个源在浏览器端都有限额的缓存数据。使用 StorageEstimate 来估算使缓存配额使用率。浏览器尽力管理硬盘空间,但它有可能会删除指定源的缓存数据。浏览器可能会删除指定源的全部数据抑或不会。切记使用名称来对脚本进行版本控制且只操做能够安全操做的脚本版本。查看 Deleting old caches 以获取更多信息。

CacheStorage 接口表示 Cache 对象存储。

接口:

  • 提供一个能够为 ServiceWorker,其它类型工做线程或者 window 做用域可访问的全部命名的缓存的主目录(虽然是在 服务工做线程 中定义的缓存,可是并不意味着只能将其和工做线程配合使用)
  • 维护一份字符名称和 Cache 对象的映射

使用 CacheStorage.open() 来建立 Cache 实例。

使用 CacheStorage.match() 来检查指定的 Request 是不是 CacheStorage 对象中的 Cache 对象的键。

使用经过全局 caches 属性来访问 CacheStorage。

IndexedDB

0_hp66xm7oe9u8ofk1

IndexedDB 是一种客户端持久性数据存储方案。因其容许开发者建立拥有富查询能力的网页程序而不用关心网络状况,这些网页程序能够线上或者离线运行。

IndexedDB 适用于大量的数据存储(好比,商业图书馆DVD 目录)和不须要保持网络连通的网页程序(好比,邮件客户端,待办事项及便笺)。

因你们都比较熟悉其它存储 API ,本文将对 IndexedDB 多唠会嗑。另外,现在随着网页程序愈来愈复杂 IndexedDB 也正变得愈来愈流行。

IndexedDB 原理

IndexedDB 容许开发者使用键来存储和获取一个对象。全部对数据库的操做均发生于事务之中。和其它网页存储方案同样,IndexedDB 遵循同源策略。所以,不可以跨域访问数据,只能访问同一个域名下的存储数据。

能够在包括 网页服务线程 的大多数上下文上使用该异步 API。IndexedDB 曾经也有 synchronous 版本,应用于网页线程中,可是因为社区对此并不感冒因此被从规范中删除了。

IndexedDB 曾经也有一个被称为 WebSQL 数据库的竞品规范,可是已经被 W3C 所弃用。虽然 IndexedDB 和 WebSQL 均是存储方案,可是它们功能并不同。WebSQL 数据库是一个关系型数据库访问系统,而 IndexedDB 只是一个索引表系统。

不要以其它类型数据库为蓝本,想固然地使用 IndexedDB。相反,须要仔细阅读文档。如下为开发者所须要了解的核心概念:

  • IndexedDB 数据库存储键-值对-值能够是复杂的结构型对象而键能够是这些对象的属性。开发者可使用对象的任意属性来建立索引进行快速搜索,好比枚举排序。键也能够是二进制型对象。
  • IndexedDB 是创建在事务型数据模型之上的-IndexedDB 中的全部操做都发生于事务上下文之中。所以,开发者不能够在事务以外执行命令或者打开游标。一样地,事务只能自动而不能够手动提交。
  • 大多数 IndexedDB 都是异步的 -API 不会经过返回值地形式来返回数据。相反,须要传入回调函数来处理返回值。意即,开发者不是同步把值存储进数据库或者直接从数据库中取回值。相反,发起 request 请求即表示一次数据库操做。当数据处理结束会通知开发者,开发者所监听的事件类型会通知数据操做是否成功。这和 XMLHttpRequest (或者其它这么多 JavaScript 相关的东西) 的工做原理大同小异。
  • IndexedDB 使用大量的请求-请求是对象用来接收以前提到的成功或者失败事件。它们包含 onsuccess 和 onerror 属性,和 readyState,result,errorCode 等用来告知请求状态的属性同样。
  • IndexedDB 是面向对象的-IndexedDB 并非一个含有表示行列集合的表关系型数据库。这一巨大的差别影响开发者设计和构建网页程序。
  • IndexedDB 不使用结构型查询语言(SQL)-它在索引上使用查询后会建立一个游标,可使用该游标来遍历结果集。若不熟悉 NoSQL 系统,能够阅读 维基百科关于 NoSQL 的文章
  • IndexedDB 也应用了同源策略-一个源即包含域名,应用程序层协议及 URL 端口地址的文档,脚本即在源中执行。每一个源都拥有其关联的数据库集。每一个数据库在源中都有惟一的标识。

IndexedDB 局限性

IndexedDB 被设计用来知足大多数的客户端存储状况的。然而,它并无被设计用来处理以下状况:

  • 国际化排序-并非全部的语言以一样的方式排列字符串,所以国际化排序是不支持的。虽然数据库并不可以以指定的国际化顺序来存储数据,开发者能够读取数据库中数据而后自行排列数据。
  • 同步- API 并非用来和服务端数据进行同步的。开发者必须本身写代码来把客户端 indexedDB 数据库和服务端数据库进行同步。
  • 全文检索-该 API 中没有和 SQL 中的 LIKE 相似的操做符。

另外,须要注意的是浏览器会在如下状况清除数据库:

  • 用户发起清除操做的请求-许多浏览器都容许用户清除指定网站的数据,包括 cookie, 书签,存储的密码以及 IndexedDB 数据。
  • 浏览器在隐私模式下-一些浏览器含有『隐私浏览』(Firefox) 和 『无痕浏览』(Chrome)模式。会话结束会清除数据库。
  • 超出了磁盘容量或者磁盘限额。
  • 数据损坏。。。

虽然现实状况和浏览器能力突飞猛进,可是浏览器产商都朝着尽一切可能保存数据的方向努力。

0_kgdqye70_z58d7na

选择合适的存储 API

正如以前所说的那样,最好尽量采用兼容性好的 API 且提供异步调用模型来最大限度地提高 UI 响应速度。这些标准天然而然会产生以下技术选择:

  • 使用 Cache API 来操做离线存储。该 API 在建立离线应用所必须的支持 服务工做线程 功能的浏览器中可用。Cache API 很是适用于排列已知 URL 的关联资源。
  • 使用 IndexedDB 来存储程序状态和用户生成的内容。和只支持 Cache API 的浏览器相比,这使得用户能够在更多的浏览器中离线使用程序。

参考

本系列持续更新中,Github 地址请查阅这里

相关文章
相关标签/搜索