Skip to content

ES Module Shims 2.0 发布公告

Refer

Source: Announcing ES Module Shims 2.0

Author: Guy Bedford

Translator: SenaoXi

Release Time: February 24, 2025

Copyright Statement

Translation and Republication Notice:

This translation is provided for educational and informational purposes only. All intellectual property rights, including copyright, remain with the original author and/or publisher. This translation maintains the integrity of the original content while making it accessible to chinese readers.

Modifications Disclosure:

  • This is a complete and faithful translation of the original content with no substantive modifications.
  • This translation includes minor adaptations to improve clarity for chinese readers while preserving all essential information and viewpoints.
  • Sections marked with [†] contain supplementary explanations added by the translator to provide cultural or technical context.

Rights Reservation:

If you are the copyright holder and believe this translation exceeds fair use guidelines, please contact us at email. We are committed to respecting intellectual property rights and will promptly address any legitimate concerns.

ES Module Shims 2.0 现已正式发布,这是一个全面的 13KB 轻量级 polyfill,支持 import maps多重 import mapsCSS 和 JSON 导入WebAssembly 模块以及 Source Phase 导入。

如果你不了解这些功能是什么,下文会有详细介绍。但首先,我想强调 2.0 版本中的一项重要新功能:TypeScript 类型擦除支持。

TypeScript类型擦除支持

为什么要在浏览器中编译 TypeScript?背景是,ES Module Shims 是一个 非常 快速的 module polyfill,目的是为了在支持基本的模块系统的浏览器上填补所有新的模块特性。因此,TC39 的类型注解提案恰好符合这个项目的 polyfill 定义。此外,通过在浏览器中使用来自 Node.jsAmaro 项目中定义的 TypeScript 变体,提供直接的逐源重写(类型擦除或可擦除语法),我们实际上获得了一个非常快速的工作流程。

它实现了 无构建 工作流 - TypeScript 作为最后一环,提供了一种无需构建工具、Node.jsnpm 的精简型 Web 开发方法。

以下是一个实际的例子,这是一个用 TypeScript 编写的 Vue 组件:

typescript
import { defineComponent } from 'vue';
import style from './user-card.css' with { type: 'css' };

document.adoptedStyleSheets.push(style);

export interface User {
  name: string;
  age: number;
}

export default defineComponent({
  props: {
    user: {
      type: Object as () => User,
      required: true
    }
  },
  template: `<div class="user-card">{{ user.name }} <span class="age">({{ user.age }})</span></div>`
});

在上面的代码中,我们不仅使用了 TypeScript,还使用了新支持的 CSS Module Scripts 特性来模块化加载组件的 CSS

css
.user-card {
  padding: 1.2rem;
  border-radius: 16px;
  margin: 1rem;
  font: 500 18px system-ui;
  width: 300px;
  background: linear-gradient(135deg, #eee 0%, #fafafa 100%);
  box-shadow: 2px 5px 7px rgba(100, 100, 255, 0.2);
  transition: transform 0.2s ease;
  cursor: pointer;
}
.user-card:hover {
  transform: translateY(-2px);
}
.age {
  color: #726497;
}

现在,使用 ES Module Shims,我们可以使用单个 HTML 文件和静态文件来运行这个应用,通过 polyfill 支持 CSS Module ScriptsTypeScript,而不需要任何构建过程或其他步骤:

html
<!doctype html>

<!-- 从你选择的CDN加载ES Module Shims -->
<script
  async
  src="https://ga.jspm.io/npm:es-module-shims@2.0.9/dist/es-module-shims.js"
></script>

<!-- 启用TypeScript和CSS导入功能(默认情况下只有import maps被polyfill) -->
<script type="esms-options">
  { "polyfillEnable": ["typescript", "css-modules"] }
</script>

<!-- 在import map中设置依赖项 -->
<script type="importmap">
  {
    "imports": {
      "vue": "https://ga.jspm.io/npm:vue@3.5.13/dist/vue.esm-browser.prod.js"
    }
  }
</script>

<div id="app">
  <user-card v-for="user in users" :key="user.name" :user="user" />
</div>

<!-- ES Module Shims会找到这个并处理剩余部分 -->
<script type="module" lang="ts">
  import { createApp } from 'vue';
  import UserCard, { type User } from './user-card.ts';

  createApp({
    setup() {
      const users: User[] = [
        { name: 'Alice', age: 25 },
        { name: 'Bob', age: 30 }
      ];
      return { users };
    }
  })
    .component('user-card', UserCard)
    .mount('#app');
</script>

你可以在 这里 查看完整示例。

支持多个 Import Map

任何使用过 import maps 一段时间的人都会知道令人恐惧的 "An import map is added after module script load was triggered."(模块脚本加载触发后添加了 import map)错误。感谢 Yoav Weiss 的努力工作,我们现在在最新版本的 Chrome 中支持多重 import maps

ES Module Shims 2.0 包含了这个功能的 polyfill,它能检测何时使用了多个 import maps,然后按照标准 polyfill 失败语义 检查模块是否依赖于新 import map 中存在但旧 import map 中不存在的映射。实际上,我们可以在单一 import maps 支持的基础上 polyfill 多重 import maps,现在可以在可能的情况下共享原生模块加载器和注册表,就像我们在旧浏览器中基于非 import maps 模块支持 polyfill import maps 本身一样。

此外,通过对 bodyhead 标签使用变异观察器(mutation observers),我们还可以检测何时动态注入 import map,然后为动态加载工作流程应用相同的 polyfill(前提是新的动态导入通过全局 importShim()polyfill 来加载)。

Wasm 模块和 Source 语义导入规范

通常,WebAssembly 是使用 fetch('./module.wasm').then(WebAssembly.compileStreaming).then(...) 这样的方式加载,来获取 WebAssembly.Module 对象进行初始化。

在这些工作流程中,由于 baseURL 语义的存在,获取 Wasm 二进制文件的 URL 并不总是那么简单。此外,构建工具可能难以与这种代码良好配合,在许多情况下需要二进制路径的运行时配置。

通过 source 语义导入规范,我们现在可以通过可移植的方式直接导入 Wasm 二进制文件,当启用该功能时,ES Module Shims 完全支持:

bash
<!doctype html>

<script
  async
  src="https://ga.jspm.io/npm:es-module-shims@2.0.9/dist/es-module-shims.js"
></script>

<!-- Enable the WebAssembly and Source Phase features -->
<script type="esms-options">
  { "polyfillEnable": ["wasm-modules", "source-phase"] }
</script>

<script type="module">
  import source mod from './module.wasm';
  const { fn } = new WebAssembly.Instance(mod, ...options...);
</script>

最后,如果你仍然好奇 ES Module Shims 是如何工作的,我之前写过一篇关于 how ES Module Shims became a production import maps polyfill 的文章。

Contributors

Changelog

Discuss

Released under the CC BY-SA 4.0 License. (1ab673e)