KV-Storage与内置模块

本期将介绍的是两个特性都还没有正式用上,但是感觉是比较有潜力的。KV Storage 是 WICG 提出的一组高效存储 API,而 built-in module 则是进入了 TC39 提案 stage1 阶段。

KV Storage

自 Chrome 74 起(包含),Chrome 在之后的几个版本开启了对 KV Storage 的短暂实验,至于为什么会出现这个东西,我找到了两个主要理由,在KV Storage Draft中说到:

The localStorage API is widely used, and loved for its simplicity. However, its synchronous nature leads to terrible performance and cross-window synchronization issues.

主要原因就是两个:1. 被广泛使用的 local Storage 竟然是同步的。2. 这个 local Storage 性能很差。

在博客KV Storage: the Web’s First Built-in Module 一文中Philip Walton做出了更详细的解释。local Storage 会导致浏览器主线程阻塞,因此性能差是其最大的问题,现代浏览器都提供了同步的 local Storage 和异步的 indexedDB,但是由于前者提供了更加简单易用的 API,而 indexedDB 使用起来相当复杂导致开发者大都使用 local Storage,这正是 KV-Storage 想要解决的问题,其兼具前两者的良好的性能和易用性。

API 的制定

KV Storage 不是一个完整的存储系统,因为 indexedDB 已经支持异步,有良好的性能,所以物尽其用,在 indexedDB 上层封装一套简单的 API 即可,同时也更“现代”,比如 indexedDB 中采用回调,新的 API 中,将采用 Promise。

不妨对比以下 local Storage 和 KV Storage:

localStorage kvStorage
getItem(key) get(key)
setItem(key, value) set(key, value)
removeItem(key) delete(key)
clear() clear()
keys()
values()
entries()

kvStorage 的 API 和 localStorage 一样简单,并且增加了几个实用的 API,与 localStorage 不同的是 indexedDB 是有「数据库名」的,同一个站点,localStorage 只有一个,但是 indexedDB 根据名字不同可以有多个,KV Storage 也是如此。它可以像 localStorage 那样直接使用,底层会自动创建一个默认 indexedDB 数据库,也可以通过实例化KVStorageArea类创建指定名称的数据库。

使用起来大概是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(async function () {
// 直接使用会自动创建数据库
await kvStorage.set("key", "value"); // 存值
console.log(await kvStorage.get("key")); // 取值
await kyStorage.clear(); // 清空

// 创建指定名字的数据库
const customDB = new KVStorageArea("custom");
await customDB.set("key", "value"); // 存值
console.log(await customDB.get("key")); // 取值
await customDB.delete("key"); // 删除
customDb.clear().then((res) => {
console.log("已清空");
});
})();

实现

KV Storage 的具体实现可以查看实现文档,主要是实现KVStorageArea类,它包含了上面提到的几个方法,每一个方法需要执行的步骤都有详细描述,也包含了一些细节内容,比如哪些是合法的 key,如何判断等,大家有兴趣可以尝试自己看着文档实现,能学到很多。

也有的地方是值得去思考的,例如数据库的创建并不是在实例化KVStorageArea类的时候发生的,而是在初次执行方法时进行实例化,类似于 lazy load,但好几个方法执行前都需要检查数据库是否存在,感觉有些繁琐,如果是我肯定就在实例化的时候创建数据库了,更为方便,而对于 KV Storage,更需要稳定性,每次执行前进行一下检查可以得到有效保证,所以事情虽然简单,细节有很多,不能说文档写的全都是对的,但至少很难出错,这就是在看文档实现的时候应该学习的。

启下

开发者可以很容易地编写一个这样的库,引用这个库后就能使用更简洁的 API 了,但是如果要让所有开发者们都用上,就必须将其变成标准或者浏览器特性,否则无法避免下载相关的 js 文件,因此博客中提到了使用 built-in module 来实现。

注:在 Chrome 74-76 版本试验中,KV Storage 实现采用 built-in module,但由于担心出现各种各样的问题,最终的 KV Storage 应该是一个全局对象。

built-in module

built-in module 直译成中文叫「内置模块」,另一个叫法是「JavaScript 标准库」,对于这个提案的两个名字说一下自己的理解,提案想将一些常用的库内置到宿主环境中(如 Node.JS、Browser),所以叫内置模块,在某个宿主中这个库就是自带的,可以称之为该宿主环境下的标准库。对于标准库的理解还是有一些牵强,大家可以评论说说自己的看法。

之所以出现这个提案,一方面是受到了其他语言的影响,比如古老的 C 语言、Python,年轻的 Go 等语言都有标准库的概念及语法,另一方面还是性能需求,如果我们要使用一个库的话,要么将其挂载到全局对象上,要么使用模块语法导入,无论如何,库的代码会增大总的代码体积,消耗更多的资源和时间去下载,虽然会有缓存,但多个执行环境无法共享,其他地方需要时还是会去下载,将常用库作为模块内置到宿主环境可以不必下载这些库,同时也可以共用他们,节约了资源也加快了速度。

提案希望在不引入新语法的前提下实现内置模块,可以很容易的想到,既然是模块那么就能使用 import 语法,只是都用 import 的话就需要区分哪些是内置模块,提案中花了很长的篇幅来说明和示例可能的区分方式,最终结果是什么现在还无法知道,目前只有 Chrome 试验过,对于内置模块,用@std/前缀来区分,我个人也是比较倾向于这种 NPM 式的模块名的。

注:究竟是使用@std/还是std:暂时无法考证,Import-Map 设计文档中使用的是@std/,而博客中则是std:

未来如何

这两个都是非正式的提案和特性,他们最终能不能成为标准无法给出确切答案,但是概率还是有的。

At TPAC 2018, the Web Platform Working Group and the IndexedDB specification editors agreed that after incubation in the WICG, KV storage should graduate to be part of the W3C IndexedDB specification.

这段话可以证实 KV Storage 是极有可能融入 indexedDB 成为 W3C 标准的,而且现在看来确实能带来很多收益。

至于 built-in module,可能需要走的路还有很长,能进入 TC39 提案 stage 1 阶段,说明思想是很好的,但目前来说,任何一个语言都是有标准的,built-in module 体现了 JavaScript 的多元化,违背了统一标准的原则,比如 Chrome 和 FireFox 之间,极有可能内置的模块不一样,导致更多麻烦。但如果离开 web 前端领域,用在 Deno 或者嵌入式设备上,肯定是更有利的,所以消除前端领域不同浏览器环境的不一致后才有可能最终成为标准。

阅读更多

  1. KV Storage 提案:github.com
  2. KV Storage 实现文档:wicg.github.io
  3. built-in module 提案:github.com
作者

KylinLee

发布于

2022-01-27

更新于

2022-02-11

许可协议

CC BY-NC-SA 4.0

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×