过去十年来,浏览器供应商和 Web 性能专家一直在说 localStorage
运行缓慢,Web 开发者应停止使用它。
公平地说,说这话的人并非不对。localStorage
是一个会阻塞主线程的同步 API,每当您访问它时,都可能会导致页面无法交互。
问题在于,localStorage
API 的简单性令人无法抗拒,而 localStorage
的唯一异步替代方案是 IndexedDB,而它(让我们面对现实)并不以易用或易于上手的 API 而闻名。
因此,开发者只能在难以使用和性能不佳之间做出选择。虽然有一些库可提供 localStorage
API 的简单性,同时实际上在后台使用异步存储 API,但在应用中添加这些库会增加文件大小,并会占用您的性能预算。
但是,如果能够同时获得异步存储 API 的性能和 localStorage
API 的简单性,而无需支付文件大小开销,该多好?
很快就会有。Chrome 正在试验一项名为内置模块的新功能,我们计划推出的第一项内置模块是一款名为 KV Storage 的异步键值对存储模块。
不过,在深入了解 KV 存储模块之前,我先解释一下什么是内置模块。
什么是内置模块?
内置模块与常规 JavaScript 模块一样,只不过它们是随浏览器一起提供的,因此无需下载。
与传统的 Web API 一样,内置模块必须经过标准化流程 - 每个模块都有自己的规范,需要经过设计审核,并获得 Web 开发者和其他浏览器供应商的明确支持,才能发布。(在 Chrome 中,内置模块将遵循我们实现和发布所有新 API 时所遵循的发布流程。)
与传统 Web API 不同,内置模块不会公开到全局范围,只能通过导入使用。
不全局公开内置模块有很多优势:它们不会增加启动新的 JavaScript 运行时上下文(例如新标签页、工作器或服务工件)的任何开销,并且除非实际导入,否则不会消耗任何内存或 CPU。此外,它们不会与代码中定义的其他变量发生名称冲突。
如需导入内置模块,请使用前缀 std:
后跟内置模块的标识符。例如,在受支持的浏览器中,您可以使用以下代码导入 KV Storage 模块(请参阅下文,了解如何在不受支持的浏览器中使用 KV Storage polyfill):
import storage, {StorageArea} from 'std:kv-storage';
KV 存储模块
KV 存储模块的简单程度与 localStorage
API 类似,但其 API 形状实际上更接近 JavaScript Map
。它使用 get()
、set()
和 delete()
,而不是 getItem()
、setItem()
和 removeItem()
。它还具有 localStorage
不支持的其他类似于映射的方法,例如 keys()
、values()
和 entries()
;与 Map
一样,它的键不必是字符串。它们可以是任何结构化可序列化类型。
与 Map
不同,所有 KV Storage 方法都会返回promise 或异步迭代器(因为与 localStorage
不同,此模块的主要特点是不同步)。如需详细了解完整 API,您可以参阅规范。
您可能已经注意到,在上面的代码示例中,KV Storage 模块有一个默认导出 storage
和一个命名导出 StorageArea
。
storage
是名为 'default'
的 StorageArea
类的实例,也是开发者在应用代码中最常使用的内容。StorageArea
类适用于需要额外隔离的情况(例如,存储数据且希望避免与通过默认 storage
实例存储的数据发生冲突的第三方库)。StorageArea
数据存储在名称为 kv-storage:${name}
的 IndexedDB 数据库中,其中 name 是 StorageArea
实例的名称。
以下示例展示了如何在代码中使用 KV 存储模块:
import storage from 'std:kv-storage';
const main = async () => {
const oldPreferences = await storage.get('preferences');
document.querySelector('form').addEventListener('submit', async () => {
const newPreferences = Object.assign({}, oldPreferences, {
// Updated preferences go here...
});
await storage.set('preferences', newPreferences);
});
};
main();
如果浏览器不支持内置模块,该怎么办?
如果您熟悉在浏览器中使用原生 JavaScript 模块,可能知道(至少到目前为止),导入除网址以外的任何内容都会生成错误。std:kv-storage
不是有效网址。
这引出了一个问题:我们是否必须等到所有浏览器都支持内置模块,才能在代码中使用内置模块?幸运的是,答案是否定的!
实际上,只要有一个浏览器支持内置模块,您就可以立即使用内置模块,这得益于我们正在实验的另一项功能,即导入映射。
导入地图
导入映射本质上是一种机制,可供开发者将导入标识符别名为一个或多个备用标识符。
这非常强大,因为它让您能够在运行时更改浏览器在整个应用中解析特定导入标识符的方式。
对于内置模块,这允许您在应用代码中引用模块的 polyfill,但支持内置模块的浏览器可以改为加载该版本!
您可以按如下方式声明导入映射,以便与 KV 存储模块搭配使用:
<!-- The import map is inlined into your page -->
<script type="importmap">
{
"imports": {
"/path/to/kv-storage-polyfill.mjs": [
"std:kv-storage",
"/path/to/kv-storage-polyfill.mjs"
]
}
}
</script>
<!-- Then any module scripts with import statements use the above map -->
<script type="module">
import storage from '/path/to/kv-storage-polyfill.mjs';
// Use `storage` ...
</script>
上述代码的关键之处在于,网址 /path/to/kv-storage-polyfill.mjs
会映射到两个不同的资源:std:kv-storage
,然后是原始网址 /path/to/kv-storage-polyfill.mjs
。
因此,当浏览器遇到引用该网址 (/path/to/kv-storage-polyfill.mjs
) 的导入语句时,会先尝试加载 std:kv-storage
,如果无法加载,则会回退到加载 /path/to/kv-storage-polyfill.mjs
。
同样,这里的奥妙之处在于,浏览器无需支持导入映射或内置模块,此技术便可正常运行,因为传递给导入语句的网址是 polyfill 的网址。polyfill 实际上并不是回退,而是默认项。内置模块是一种渐进式增强功能!
如果浏览器完全不支持模块,该怎么办?
若要使用导入映射有条件地加载内置模块,您实际上必须使用 import
语句,这也意味着您必须使用模块脚本,即 <script type="module">
。
目前,超过 80% 的浏览器支持模块,对于不支持模块的浏览器,您可以使用 module/nomodule 技术来提供旧版软件包。请注意,生成 nomodule
build 时,您需要添加所有 polyfill,因为您可以肯定,不支持模块的浏览器肯定也不支持内置模块。
KV 存储演示
为了说明您可以在支持旧版浏览器的同时使用内置模块,我编写了一个演示版,其中包含上述所有技术,并且可以在所有现有浏览器中运行:
- 支持模块、导入映射和内置模块的浏览器不会加载任何不需要的代码。
- 支持模块和导入映射但不支持内置模块的浏览器会加载 KV Storage polyfill(通过浏览器的模块加载器)。
- 支持模块但不支持导入映射的浏览器也会加载 KV 存储空间 polyfill(通过浏览器的模块加载器)。
- 完全不支持模块的浏览器会在其旧版软件包(通过
<script nomodule>
加载)中获取 KV 存储空间 polyfill。
该演示在 Glitch 上托管,因此您可以查看其源代码。我还在README中详细说明了实现方式。如果您想了解其构建方式,欢迎随时查看。
如需实际查看原生内置模块的运作方式,您必须在 Chrome 74 或更高版本中加载该演示版,并启用实验性 Web 平台功能标志 (chrome://flags/#enable-experimental-web-platform-features
)。
您可以验证是否正在加载内置模块,因为您不会在 DevTools 的“源代码”面板中看到 polyfill 脚本;而是会看到内置模块版本(有趣的是:您实际上可以检查模块的源代码,甚至可以在其中设置断点!):
欢迎向我们提供反馈
通过本简介,您应该已经了解了内置模块的可能用途。希望您也对此感到兴奋!我们非常希望开发者试用 KV Storage 模块(以及本文中讨论的所有新功能),并向我们提供反馈。
您可以通过以下 GitHub 链接针对本文中提及的每项功能向我们提供反馈:
如果您的网站目前使用 localStorage
,您应尝试改用 KV Storage API,看看它是否能满足您的所有需求。如果您注册 KV Storage 源代码试用版,则可以立即部署这些功能。所有用户都应该能受益于更出色的存储性能,Chrome 74 及更高版本的用户无需支付任何额外的下载费用。