JavaScript是如何工做的:存储引擎+如何选择合适的存储API

这是专门探索 JavaScript 及其所构建的组件的系列文章的第 16 篇。javascript

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!前端

若是你错过了前面的章节,能够在这里找到它们:java

  1. JavaScript 是如何工做的:引擎,运行时和调用堆栈的概述!
  2. JavaScript 是如何工做的:深刻V8引擎&编写优化代码的5个技巧!
  3. JavaScript 是如何工做的:内存管理+如何处理4个常见的内存泄漏!
  4. JavaScript 是如何工做的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!
  5. JavaScript 是如何工做的:深刻探索 websocket 和HTTP/2与SSE +如何选择正确的路径!
  6. JavaScript 是如何工做的:与 WebAssembly比较 及其使用场景!
  7. JavaScript 是如何工做的:Web Workers的构建块+ 5个使用他们的场景!
  8. JavaScript 是如何工做的:Service Worker 的生命周期及使用场景!
  9. JavaScript 是如何工做的:Web 推送通知的机制!
  10. JavaScript是如何工做的:使用 MutationObserver 跟踪 DOM 的变化!
  11. JavaScript是如何工做的:渲染引擎和优化其性能的技巧!
  12. JavaScript是如何工做的:深刻网络层 + 如何优化性能和安全!
  13. JavaScript是如何工做的:CSS 和 JS 动画底层原理及如何优化它们的性能!
  14. JavaScript的如何工做的:解析、抽象语法树(AST)+ 提高编译速度5个技巧!
  15. JavaScript是如何工做的:深刻类和继承内部原理+Babel和 TypeScript 之间转换!

概述

在设计 Web 应用程序时,为本地浏览器选择合适的存储机制相当重要, 一个好的存储引擎能够确保可靠地保存信息,减小带宽,提升响应能力。正确的存储缓存策略是实现离线移动 Web 体验的核心构建块,同时也大大的提升了用户体验。git

在本章中,讨论可选择的存储 Api 和服务,并提供一些在构建 Web应用程序,该使用哪一种存储引擎。github

数据模型

数据存储模型肯定数据在内部的组织方式,这会影响 Web 应用程序的整个设计,合理的数据模式会让 Web 应用程序在完成它应有的任务下还能让运行速度更加高效。对于全部与工程相关的问题,没有存在最好的解决方法,也没有适用于全部问题的解决方案,不一样场景下有不一样的选择。因此,来看看可选择的数据模型:web

  • 结构化: 存储在具备预约义字段的表中的数据(这是典型的基于 SQL 的数据库管理系统)适行灵活的动态查询。浏览器中结构化数据存储的一个表明的例子是 IndexedDB
  • Key/Value: 键/值 数据存储和相关的 NoSQL 数据库提供了存储和检索由惟一键索引的非结构化数据的能力。键/值 数据存储相似于哈希表,由于它们容许对索引的不透明数据进行长时间访问。 键/值 数据存储的表明例子是浏览器中的 Cache API 和服务器上的 Apache Cassandra
Apache Cassandra 是一套开源分布式数据库管理系统,由Facebook开发,用于储存特别大的数据。
  • 字节流:这个简单的模型将数据存储为长度不透明的字节字符串变量,将任何形式的内部组织留给应用层。这个模型特别适合于文件系统和其余分层组织的数据块。字节流数据存储的表明例子包括文件系统和云存储服务。

持久化

web 应用程序的存储方法能够根据数据持久化的时间段进行划分:数据库

  • 会话持久化: 该类别中的数据仅在单个 Web 会话或浏览器选项卡保持激活状态时才持久,具备会话持久性的存储机制的一个示例是 Session Storage API
  • 设备的持久化: 此类别中的数据在特定设备上跨会话和浏览器选项卡/窗口持久化,具备设备持久化的存储机制的一个示例是 Cache API
  • 此类中的数据跨会话和设备持久化。所以,它是最健壮的数据持久性形式。可是,它不能存储在设备自己上,这意味须要在某种服务器端存储。在这里不会详细讨论它,由于本文的重点是在设备自己上存储数据。

浏览器中的数据持久化

如今,有至关多的浏览器 Api 用来存储数据。这里将逐一介绍其中的一些及它们的区别,以便后续咱们可以容合理的选择使用。编程

然而,在选择如何持久化数据以前,有几件事须要考虑。固然,有必要知道的的第一件事是你的 Web 应用程序应用场景是什么,以及之后如何迭代和丰富。即便你知道了这些,最终也会有几个选择。因此,如下是须要了解的:segmentfault

  • 浏览器支持  —  标准化和完善的 API 更值得咱们选择,由于它们每每寿命更长,支持更普遍, 这些API 还享有更丰富的文档和开发人员社区。
  • 事务 — 有时,相关存储操做的集合原子地成功或失败是很重要的。传统上,数据库使用事务模型支持此功能,其中相关更新能够分组到任意单元中。
  • 同步/异步 — 有些存储 Api 是同步的,由于存储或检索请求会阻塞当前活动的线程,直到请求完成。使用同步存储 API 会阻塞主线程,并为 Web 应用程序的 UI 建立冻结体验。若是可能,使用异步API。

比较

在本节中,了解决 Web 开发人员的当前可用存储 Api,并从各个维度上进行比较。api

图片描述

文件系统API

图片描述

经过 FileSystem API, Web 应用就能够建立、读取、导航用户本地文件系统中的沙盒部分以及向其中写入数据。

API 被分为如下不一样的主题:

  • 读取和处理文件:File/Blob、FileList、FileReader
  • 建立和写入:BlobBuilder、FileWriter
  • 目录和文件系统访问:DirectoryReader、FileEntry/DirectoryEntry、LocalFileSystem

FileSystem API 是非标准 API。在发布环境因慎重使用,由于并是全部的浏览器都支持,实现方式可能存在很大的不兼容性,而且在未来可能也会发生变化。

请求文件系统

网络应用可经过调用 window.requestFileSystem() 请求对沙盒文件系统的访问权限:

// Note: The file system has been prefixed as of Google Chrome 12:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(type, size, successCallback, opt_errorCallback)

type:文件存储是否应该是持久的。可能的值包括 window.TEMPORARYwindow.PERSISTENT。经过 TEMPORARY 存储的数据可由浏览器自行决定删除(例如在须要更多空间的状况下),要清除PERSISTENT 存储,必须得到用户或应用的明确受权,而且须要用户向你的应用授予配额。

size: 应用须要用于存储的大小 (以字节为单位)。

successCallback:文件系统请求成功时调用的回调,其参数为 FileSystem 对象。

opt_errorCallback: 用于处理错误或获取文件系统的请求遭到拒绝时可选的回调,其参数为 FileError 对象。

若是你是首次调用 requestFileSystem(),系统会为你的应用建立新的存储。请注意,这是沙箱文件系统,也就是说,一个网络应用没法访问另外一个应用的文件。

在访问文件系统以后,能够对文件和目录执行大多数标准操做。

与其余存储类型相比,文件系统是一个彻底不一样的存储类型,由于它的旨在知足数据库,很不能很好地服务的客户端存储用例。一般,这些应用程序处理大型二进制blob或与浏览器上下文以外的应用程序共享数据。

如下使用文件系统 API 的几个示例:

  • 有上传的应用

    • 当你选择一个文件或目录进行上传时,你能够赋值文件到一个本地沙盒并一次上传一个块。
    • 应用能够在一次中断后从新上传,中断可能包括浏览器被关闭或崩溃,链接中断,或电脑被关闭。
  • 视频游戏或其余使用大量媒体资源的应用

    • 用下载一个或多个大压缩包并在本地将他们解压到一个文件目录中。
    • 应用能在后台预取资源,从而让用户可以进入下一项工做或游戏等级,而不须要等待下载。
  • 音频或照片编辑器使用线下访问或本地缓存

    • 应用能够分段写入文件(例如只覆盖ID3/EXIF标签而不是整个文件)。
  • 线下视频浏览

    • 应用能够访问只下载了部分的文件。
  • 线下网络邮件客户端

    • 客户端下载附件并在本地存储它们。
    • 客户端缓存附件用于稍后的上传。

目前浏览器对文件系统 API 的支持:

图片描述

Local storage

图片描述

只读的 localStorage 容许你访问一个 Document 的远端(origin)对象 Storage;其存储的数据能在跨浏览器会话保留。 localStorage 相似 sessionStorage,其区别在于:存储在 localStorage 的数据能够长期保留;而当页面会话结束——也就是说当页面被关闭时,存储在 sessionStorage 的数据会被清除 。

应注意不管数据存储在 localStorage 仍是 sessionStorage ,它们都特定于页面的协议

另外,localStorage 中的键值对老是以字符串的形式存储。

当前浏览器对API的支持:

图片描述

Session storage

图片描述

sessionStorage 属性容许你访问一个 session Storage 对象。它与 localStorage 类似,不一样之处在于 localStorage 里面存储的数据没有过时时间设置,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,而且从新加载或恢复页面仍会保持原来的页面会话。在新标签或窗口打开一个页面时会在顶级浏览上下文中初始化一个新的会话,这点和 session cookies 的运行方式不一样。

应该注意的是,不管是 localStorage 仍是 sessionStorage 中保存的数据都仅限于该页面的协议

当前浏览器对API的支持:

图片描述

Cookies

图片描述

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。一般,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登陆状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie主要用于如下三个方面:

  • 会话状态管理(如用户登陆状态、购物车、游戏分数或其它须要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)

* 浏览器行为跟踪(如跟踪分析用户行为等)

Cookie曾一度用于客户端数据的存储,因当时并无其它合适的存储办法而做为惟一的存储手段,但如今随着现代浏览器开始支持各类各样的存储方式,Cookie渐渐被淘汰。因为服务器指定Cookie后,浏览器的每次请求都会携带Cookie数据,会带来额外的性能开销(尤为是在移动环境下)。

cookie 类型有两种:

  • 会话 Cookie  —  浏览器关闭以后它会被自动删除,也就是说它仅在会话期内有效。会话期Cookie不须要指定过时时间(Expires)或者有效期(Max-Age)。须要注意的是,有些浏览器提供了会话恢复功能,这种状况下即便关闭了浏览器,会话期Cookie也会被保留下来,就好像浏览器历来没有关闭同样。
  • 持久 Cookie — 和关闭浏览器便失效的会话期Cookie不一样,持久性Cookie能够指定一个特定的过时时间(Expires)或有效期(Max-Age)。

当机器处于不安全环境时,切记不能经过HTTP Cookie存储、传输敏感信息,且全部浏览器都普遍支持cookie。

Cache

图片描述

Cache 接口为缓存的 Request/Response 对象对提供存储机制,例如,做为 ServiceWorker 生命周期的一部分。请注意,Cache 接口像 workers 同样,是暴露在 window 做用域下的。尽管它被定义在 service worker 的标准中, 可是它没必要必定要配合 service worker 使用.

一个域能够有多个命名 Cache 对象。你须要在你的脚本 (例如,在 ServiceWorker 中)中处理缓存更新的方式。除非明确地更新缓存,不然缓存将不会被更新;除非删除,不然缓存数据不会过时。使用 CacheStorage.open(cacheName) 打开一个Cache 对象,再使用 Cache 对象的方法去处理缓存.

你须要按期地清理缓存条目,由于每一个浏览器都硬性限制了一个域下缓存数据的大小。缓存配额使用估算值,可使用 StorageEstimate API 得到。浏览器尽其所能去管理磁盘空间,但它有可能删除一个域下的缓存数据。浏览器要么自动删除特定域的所有缓存,要么所有保留。确保按名称安装版本缓存,并仅从能够安全操做的脚本版本中使用缓存。查看 Deleting old caches 获取更多信息.

CacheStorage 接口表示 Cache 对象的存储。

  • 它提供了一个 ServiceWorker,其它类型worker或者 window 范围内能够访问到的全部命名cache的主目录(它并非必定要和 service workers 一块儿使用,即便它是在 service workers 规范中定义的),并维护一份字符串名称到相应 Cache 对象的映射。
  • 使用 CacheStorage.open() 获取 Cache 实例。
  • 使用 CacheStorage.match() 检查给定的 Request 是不是 CacheStorage 对象跟踪的任何 Cache 对象中的键。

你能够经过 caches 属性访问 CacheStorage .

IndexedDB

图片描述

IndexedDB 是一种在用户浏览器中持久存储数据的方法。由于它容许你建立具备丰富查询功能的 Web 应用程序,不管网络可用性如何,这些应用程序均可以在线和离线工做。IndexedDB 对于存储大量数据的应用程序(例如,借出库中的 DVD 目录)和不须要持久 internet 链接才能工做的应用程序(例如,邮件客户机、待办事项列表和记事本)很是有用。

在本文中,会更详细地讨论存储数据库,由于其他的存储 Api 都是众所周知的。另外,随着 Web 应用程序的复杂性愈来愈高,IndexedDB 也愈来愈受欢迎。

IndexedDB的内部结构

IndexedDB 经过“键”来存储和检索对象。对数据库所作的全部更改都发生在事务中,像大多数 Web 存储解决方案同样,IndexedDB 遵循同源策略。所以,虽然能够访问域中存储的数据,可是不能跨不一样的域访问数据。

IndexedDB 是一个 异步 API,能够在大多数上下文中使用,包括 WebWorkers。它过去也包括一个同步版本,供 Web 开发者使用,可是因为 Web 社区对它缺少兴趣,因此从规范中删除了这个版本。

IndexedDB 曾经有一个与之竞争的规范,称为 WebSQL 数据库,可是 W3C 弃用了它。虽然 IndexedDB 和WebSQL 都是存储解决方案,但它们提供的功能不一样。WebSQL 数据库是一个关系数据库访问系统,而IndexedDB 是一个索引表系统。

不要一开始就使用 IndexedDB,这依赖于你对其余类型数据库的假设。相反,应该仔细阅读文档,如下是一些须要牢记的基本概念:

  • IndexedDB 数据库使用 key-value 键值对储存数据  —  values 数据能够是结构很是复杂的对象,key能够是对象自身的属性。你能够对对象的某个属性建立索引(index)以实现快速查询和列举排序。key能够是二进制对象。
  • IndexedDB 是事务模式的数据库 —  任何操做都发生在事务(transaction)中。 IndexedDB API提供了索引(indexes)、表(tables)、指针(cursors)等等,可是全部这些必须是依赖于某种事务的。所以,你不能在事务外执行命令或者打开指针。事务(transaction)有生存周期,在生存周期之后使用它会报错。而且,事务(transaction)是自动提交的,不能够手动提交。
  • The IndexedDB API 基本上是异步的 — IndexedDB 的 API 不经过 return 语句返回数据,而是须要你提供一个回调函数来接受数据。执行 API 时,你不以同步(synchronous)方式对数据库进行“存储”和“读取”操做,而是向数据库发送一个操做“请求”。当操做完成时,数据库会以DOM事件的方式通知你,同时事件的类型会告诉你这个操做是否成功完成。这个过程听起来会有些复杂,可是里面是有明智的缘由的。这个和 XMLHttpRequest 请求是相似的。
  • IndexedDB数据库“请求”无处不在 — 每个“请求”都包含 onsuccessonerror 事件属性,同时你还对 “事件” 调用 addEventListener()removeEventListener()。“请求” 还包括 readyStateresulterrorCode 属性,用来表示“请求”的状态。result 属性尤为神奇,他能够根据“请求”生成的方式变成不一样的东西,例如:IDBCursor 实例、刚插入数据库的数值对应的键值(key)等。
  • IndexedDB是面向对象的 — indexedDB 不是用二维表来表示集合的关系型数据库,这一点很是重要,将影响你设计和创建你的应用程序。
  • indexedDB 不使用结构化查询语言(SQL) — 它经过索引(index)所产生的指针(cursor)来完成查询操做,从而使你能够迭代遍历到结果集合。若是你不熟悉NoSQL系统,能够参考维基百科相关文章
  • IndexedDB遵循同源(same-origin)策略 — “源”指脚本所在文档URL的域名、应用层协议和端口。每个“源”都有与其相关联的数据库。在同一个“源”内的全部数据库都有惟1、可区别的名称。

IndexedDB局限性

如下状况不适合使用IndexedDB

  • 全球多种语言混合存储。国际化支持很差。须要本身处理。
  • 和服务器端数据库同步。你得本身写同步代码。
  • 全文搜索。IndexedDB 接口没有相似 SQL 语句中 LIKE 的功能。

注意,在如下状况下,数据库可能被清除:

  • 用户请求清除数据。
  • 浏览器处于隐私模式。最后退出浏览器的时候,数据会被清除。
  • 硬盘等存储设备的容量到限。
  • 数据损坏。
  • 进行与特性不兼容的操做。
  • 确切的环境和浏览器特性会随着时间改变,但浏览器厂商一般会遵循尽最大努力保留数据的理念。

确切的环境和浏览器特性会随着时间改变,但浏览器厂商一般会遵循尽最大努力保留数据的理念。

图片描述

选择正确的存储API

如前所述,最好选择尽量多的浏览器普遍支持的 Api,并提供异步调用模型,以最大限度地提升 UI 响应能力。这些标准天然会致使如下技术选择:

  • 对于离线存储,请使用 Cache API。任何支持建立离线应用程序所需的 Service Worker technology 的浏览器均可以使用这个 API,Cache API 很是适合存储与已知 URL 关联的资源。
  • 要存储应用程序状态和用户生成的内容,请使用IndexedDB。这使得用户能够在更多的浏览器中离线工做,而不只仅是那些支持缓存API的浏览器。


原文:

https://blog.sessionstack.com...

这篇主要一些内容原做者大部分是经过 MDN 整理的组合的,我也是根据中文的 MND 整理的组合。

你的点赞是我持续分享好东西的动力,欢迎点赞!

欢迎加入前端你们庭,里面会常常分享一些技术资源。

clipboard.png