仓库源文站点原文


title: KV Storage,Web 上第一个内置模块 description: 小胡子哥的个人网站 warning: true author: 小胡子哥 tags:


原文地址:KV Storage, the Web's First Built-in Module 原文时间:2019年3月 翻译计划:https://github.com/barretlee/translation-plan 翻译人员:小胡子哥

在过去十年中,浏览器供应商和网络性能专家一直在说 localStorage 很慢,网络开发者应该停止使用它。

说实在的,确实如此,localStorage 是一个阻止主线程的同步 API,当你使用它的时候它可能会影响页面之间的交互。

问题在于 localStorage API 的设计十分简单,唯一能够替代 localStorage 的异步方案只有 IndexedDB,而它却因为 API 易用性设计较差鲜为人知。

因此开发者需要在难用的 IndexedDB 和有性能问题的 localStorage 中作出抉择。虽然有些库提供了 localStorage API 的简洁性,同时又采用异步调用来解决性能问题,但是在网页应用中,一个库本身的文件大小所包含的网络下载开销和 JS 解析开销也会影响到性能。

是否可以有这么一个库,既提供异步调用的 Storage API,性能方面又比较良好,并且我们使用它的时候还不需要支付文件大小开销呢?

答案是 Yes!Chrome 正在实验一个 build-in 模块的新功能,我们计划发布了第一个名为 KV Storage 的 build-in 模块,它就具备异步的 key/value Storage API。

在我介绍 KV Storage 之前,先给大家介绍下什么是 build-in 模块。

什么是 build-in 模块?

build-in 模块和普通的 JavaScript 模块一样,只不过它无需下载,原生内置在浏览器中。

与传统的 Web API 一样,内置模块必须经过标准化和具有明确定义的规范化;但与传统 Web API 不同的是,它们不会暴露在全局范围内,可以通过 import 方式获取。这样会带来一些优势:

要导入 build-in 模块,需要使用前缀 std:,后面紧接着 build-in 模块的名字。在支持 build-in 模块的浏览器中,你可以通过如下方式导入一个 KV Storage 模块:

import {storage, StorageArea} from 'std:kv-storage';

KV Storage 模块

KV Storage 模块的 API 与 localStorage 是相似的,只不过它的 API 有点像 JavaScript 的 Map,提供的是 get()set()delete(),而非 getItem()setItem()removeItem(),另外它还有几个 localStorage 不具备方法,如 keys()values()entries(),与 Map 相似,它的 key 没有限定必须为 string 类型,可以是任何结构化可序列化类型。

但是与 Map 不同的是,KV Storage 返回的结果是 promiseasync iterators 类型,详细的 API 介绍,可以戳 这里

你可能注意到了上面的代码,包含 storageStorageArea 两个导出对象,storage 是一个 StorageArea 类的实例,名为 default,也会是开发者未来在代码中用的最频繁的一个导出对象。StorageArea 类是为需要额外隔离的场景提供的,比如三方库中的储存数据为了避免与 default 实例产生冲突的时候就需要重新创建一个实例。StorageArea 数据被储存在 IndexedDB 数据库中,取名为 kv-storage:${name},这里的 nameStorageArea 的实例名称。下面就是一个如何使用 KV Storage 的例子:

import {storage} from 'std:kv-storage';

const main = async () => {
  const oldPreferences = await storage.get('preferences');

  document.querySelector('form').addEventListener('submit', () => {
    const newPreferences = Object.assign({}, oldPreferences, {
      // Updated preferences go here...
    });

    await storage.set('preferences', newPreferences);
  });
};

main();

这里需要注意的是,KV Storage 目前只有 Chrome 74+ 版本才支持,你需要在实验室打开对应的开关:chrome://flags/#enable-experimental-web-platform-features.

浏览器不支持 KV Storage 时如何处理?

如果你熟悉在浏览器中使用原生 JavaScript 模块,你可能知道(至少到目前为止)导入除 URL 以外的任何内容都会产生错误。而 std:kv-storage 就不是有效的 URL。

那么,这里我们抛出一个问题:我们是否需要等待所有的浏览器都支持 build-in 模块的时候才开始在代码中使用它呢?

答案当然是 NO!你现在就可以配合 import maps 在你的代码中使用 build-in 模块!

import maps

import maps 本质上是一种机制,开发人员可以通过该机制将 import 的包别名为一个或多个备用标识。这非常有用,它提供了一种方法,可以让浏览器在运行时更改代码中 import 的内容。

这就为 build-in 模块提供了一种 polyfill 的机制,具体如下所示:

<!-- 定义好 import map -->
<script type="importmap">
{
  "imports": {
    "/path/to/kv-storage-polyfill.mjs": [
      "std:kv-storage",
      "/path/to/kv-storage-polyfill.mjs"
    ]
  }
}
</script>

<!-- 在 import 执行时会读取 map 配置-->
<script type="module">
  import {storage} from '/path/to/kv-storage-polyfill.mjs';

  // 使用 `storage` ...
</script>

上面的代码中,/path/to/kv-storage-polyfill.mjs 被映射到了两种不同的资源 std:kv-storage 和一个 URL 地址 /path/to/kv-storage-polyfill.mjs,当浏览器解析到 import 时,如果浏览器支持 std:kv-storage,那么就会直接加载它,否则便会降级使用 /path/to/kv-storage-polyfill.mjs

这里的妙处就在于,不支持 build-in 模块的浏览器在执行这句代码的时候,真正引用的是 /path/to/kv-storage-polyfill.mjs,这并非一种回退降级机制,所以说,build-in 是渐进增强的一种方案。

如果浏览器连 type=module 都不支持呢?

为了说明今天可以使用内置模块同时仍然支持旧版浏览器,我已经整合了一个包含上述所有技术的Demo

你可以通过打开 Devtool->sources 查看是否存在 kv-storage 来验证 build-in 模块是否被成功加载了:

upload successful

给我们反馈

这篇文章带着大家认识了 build-in 模块,相信你已经有点激动了!我们殷切期望开发者能够立即尝试使用 KV Storage,并给我们提出反馈意见。

如果你的网站正在使用 localStorage,你可以尝试切换到 KV Storage API,另外,你可以在 KV Storage origin trial 上注册,这样你就可以立马让你的切换生效了!让你所有的用户享受更好的性能吧,使用 Chrome 74+ 用户不会支付额外的 polyfill 下载开销。


译者有话说

这篇文章要阐述的内容还是挺易懂的,只不过逐字逐句翻译起来,反而读起来有点懵逼了。以前我基本都是看几篇文章把知识点消化以后,自己整理,不过为了翻译的「原滋原味」,还是忍了。

Chrome 拿出 12 年的一篇旧文,非说 localStorage 存在不好的性能问题,然后把 KV Storage 搬出来,这个逻辑虽然牵强了些,不过还是可以理解它的初衷——在 build-in module 这块进行初步的尝试。

其实未来这块会存在很多的问题,我稍微列几个:

目前 std 也还在 stage 1 阶段,未来是否能够保留下来,或者保留下来是否有很大的变化,我们还不知道。不过从这种特性来看,Web 的前途还是不错的。