在浏览器中内嵌word_KV Storage: 浏览器第一个内嵌模块

90dac593f0b4a128e2156fabaff47f37.png

LocalStorage的性能问题过去一直被人们所诟病,甚至连浏览器厂商和WEB性能专家们都呼吁web开发者停止使用。

诚然,人们说的也并非没有道理。localStorage是会阻塞主线程的同步API,任何时候使用它都有可能使你的页面处于不可交互的状态。

而相对于localStorage通俗易懂的API设计,唯一可替代localStorage的异步API就是IndexDB了,它的设计可谓复杂不少。

这就为Web开发者留下了一个难题,我应该使用较难理解的IndexDB还是选择简单但体验较差的localStorage呢?针对这个问题,不少既能进行读写又能提供通俗易懂的API第三方库开始涌现,然而它们的底层也是基于异步存储API来开发的。引入这些库不仅带来了额外的文件体积,甚至还带来了影响页面性能的风险。

问题来了,是否存在这样的一个可能性,开发者既能享受有如localStorage简单的API,同时又能拥有像异步存储API的性能呢?

答案是,很快就会有了。Chrome正在实验一个名为内嵌模块的新特性,而首个基于新特性登陆的功能就是名为KV Storage的异步key/value储存模块。

什么是内嵌模块?

内嵌模块就像普通的Javascript模块,但它不需要随着页面打包让用户下载,因为浏览器自身已经承载了这个模块。

与传统的web APIs一样,内嵌模块这个特性也需要经历一系列标准化的过程 — 在每个新特性落实前,都需要先提供一份规范,通过由浏览器厂商及web开发者组成的设计评审并获得许可。(在Chrome里面,内嵌模块也同样需要遵循与过往推出新API一样的流程。)

但内嵌模块并不像传统的web APIs一样暴露在全局对象,相反,它只能通过import引用。

不把内嵌模块暴露在全局有很多好处:在创建Javascript运行上下文的时候不会被无故引入(例如:打开new tab、worker或者service worker),此外也不会消耗多余的内存和CPU资源,直到模块确实被引用了。除此之外,这样做也免去了与开发者代码中全局变量命名冲突的风险。

要引用一个内嵌模块,你需要在需引用模块的名字前加上std:的前缀。举个例子,在支持的浏览器中,你可以使用下面的代码引入KV Storage

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

KV Storage

KV Storage简单得有点像localStorage的API,但它的API模型其实与Javascript的Map更为接近。为了替代getItem,setItemremoveItem,它提供了get,setdelete。除此之外,也有一系列localStorage不具备的类Map方法,例如keys,values,和entries。与Map相似,这里的键不要求必须是字符串,只要是可序列化的类型都可以。

和Map不一样的是,所有KV Storage的方法均返回Promise或者Async Iterators(模块的设计核心并不是同步操作,这一点和localStorage是不一样的)。感兴趣的同学可以从这个文档中获取更多API的完整细节。

或者你已经从上面的代码实例注意到,KV Storage暴露了两个变量:storageStorageArea

事实上,storage可以被理解成StorageArea的一个实例,此时的namedefault,这也是开发者最常用的一个用法。而StorageArea这个类则是为分区存储而生,它会把数据存储到名为kv-storage:${name}的IndexedDB数据库中,这里的name对应StorageArea实例的name

下面是KV Storage的使用例子:

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();
注意: KV Storage在Chrome 74已经可以通过打开 Experimental Web Platform features来使用了(chrome://flags/#enable-experimental-web-platform-features)

要是浏览器不支持内嵌模块怎么办?

如果你已经熟练使用浏览器原生Javascript模块,你应该知道import URL之外的东西会产生异常,而std:kv-storage并不是一个合法的URL。

那么我们就非得等到所有浏览器支持内嵌模块才能使用了么?当然不是。

事实上,得益于另一个在实验阶段的新特性import maps,只要浏览器支持我们也可以使用。

Import maps

Import maps本质上是一个可以让开发者将import标识符映射到一到多个其他标识符的机制,这个机制非常强大,因为它赋予了开发者在运行时针对指定的import模块动态修改浏览器实际获取模块的能力。

针对内嵌模块的场景,不支持内嵌模块的浏览器就可以通过引入polyfill来使用KV Storage了,而支持内嵌模块的浏览器则直接引用。

下面是通过声明import map使用KV Storage的一个例子:

<!-- 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。浏览器在处理引用这个URL(/path/to/kv-storage-polyfill.mjs)的import声明时,首先会尝试加载std:kv-storage,如果失败会降级到加载/path/to/kv-storage-polyfill.mjs

值得一提的是,这里的巧妙之处在于浏览器并不一定要支持import maps内嵌模块,因为import引用的URL已经是polyfill了。在不支持的浏览器环境下,Polyfill是默认引用的模块,在支持的浏览器环境下又能切换回原生的KV Storage,这让内嵌模块成为了一个渐进式增强的能力。

如果浏览器完全不支持modules呢?

为了使用import map实现按需加载内嵌模块,你必须使用import声明,这意味着你需要使用module scripts,即<script type="module">

如今已经有超过80%的浏览器支持modules了,至于剩余不支持的浏览器,可以提供nomodule的bundle,此时你需要将所有polyfill模块打包到bundle内。

KV Storage demo

为了证明内嵌模块在较老的浏览器仍能得到兼容,这里提供一个包含上述所提及所有内容的Demo来在现今各个类型的浏览器运行:

  • 支持modules, import maps, 和内嵌模块的浏览器不需要加载任何冗余代码
  • 支持modules, import maps但不支持内嵌模块的浏览器会加载KV Storage的polyfill(通过浏览器的module loader)
  • 支持modules但不支持import maps的浏览器也加载KV Storage的polyfill(通过浏览器的module loader)
  • 连modules也不支持的浏览器则加载包含KV Storage polyfill的bundle(通过<script nomodule>)

Demo已经部署到Glitch,所以任何人都可以看到它的源码。README还附带了一些细节的解释,欢迎感兴趣的同学抽空阅览。

Demo使用了Rollup作为构建工具,生成了多个版本,分别适用于上述几个类型的浏览器。

如果想看到原生的内嵌模块,你需要使用Chrome 74(目前的Chrome Dev或Canary版本)并打开Experimental Web Platform features(chrome://flags/#enable-experimental-web-platform-features)。

你会发现内嵌模块确实被加载了,因为在这里并没有加载polyfill(有趣的是你甚至可以看到模块的源码并且进行断点。

d7e41bcd94be5c0f40a2d18adba11c97.png

关于反馈

上述的介绍应该让不少人体验到内嵌模块的魅力,希望你会为此感到兴奋。希望更多的开发者能够尝试KV Storage(还有上面提及到的其余特性)并给出反馈。

下面是上述特性的Github地址:

  • KV Storage
  • KV Storage Polyfill
  • Build-in modules
  • Import Maps
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值