发布

本页面提供了将 Lit 组件发布到 npm 的指南,npm 是绝大多数 JavaScript 库和开发者使用的包管理器。查看入门套件了解设置好用于发布到 npm 的可重用组件模板。

要将你的组件发布到 npm,请参见贡献 npm 包的说明

你的 package.json 配置应该有 typemainmodule 字段:

package.json

你还应该创建一个 README,描述如何使用你的组件。

我们建议以标准 ES2021 语法发布 JavaScript 模块,因为这在所有常青浏览器上都受支持,并且能产生最快和最小的 JavaScript。你的包的使用者可以随时使用编译器来支持旧版浏览器,但如果你在发布前预编译代码,他们就无法将旧版 JavaScript 转换为现代语法。

然而,重要的是,如果你使用新提出的或非标准的 JavaScript 特性,如 TypeScript、装饰器和类字段,你_应该_在发布到 npm 之前将这些特性编译为浏览器原生支持的标准 ES2021。

以下 JSON 示例是一个部分的 tsconfig.json,它使用推荐的选项来目标设定 ES2021,启用装饰器编译,并为用户输出 .d.ts 类型:

tsconfig.json

注意,将 useDefineForClassFields 设置为 false 只在 target 设置为 es2022 或更高版本(包括 esnext)时才需要,但建议明确确保此设置为 false

从 TypeScript 编译时,你应该在 package.jsontypes 字段中包含组件类型的声明文件(基于上面的 declaration: true 生成),并确保 .d.ts.d.ts.map 文件也被发布:

package.json

更多信息请参见 tsconfig.json 文档

要编译使用尚未包含在 ES2021 中的提议 JavaScript 特性的 Lit 组件,请使用 Babel。

安装 Babel 和你需要的 Babel 插件。例如:

配置 Babel。例如:

babel.config.json

你可以调整 "targets" 选项来目标设定你想要支持的浏览器。查看 @babel/preset-env 了解可用选项。

你可以通过像 @rollup/plugin-babel 这样的打包器插件运行 Babel,或者从命令行运行。更多信息请参见 Babel 文档

以下是发布可重用 Web 组件时要遵循的其他良好实践。

不要在模块中导入 polyfills

Permalink to "不要在模块中导入 polyfills"

Polyfills 是应用程序的关注点,所以应用程序应该直接依赖它们,而不是单个包。所需的确切 polyfills 通常取决于应用程序需要支持的浏览器,这个选择最好留给使用你的组件的应用程序开发者。你的组件文档应该清楚地标识它使用的可能需要 polyfills 的任何 API。

包可能需要依赖 polyfills 来进行测试和演示,所以如果需要它们,它们应该只放在 devDependencies 中。

不要打包、压缩或优化模块

Permalink to "不要打包、压缩或优化模块"

打包和其他优化是应用程序的关注点。在发布到 npm 之前打包可重用组件也可能会在用户的应用程序中引入多个版本的 Lit(和其他包),因为 npm 无法去重这些包。这会导致膨胀并可能导致错误。

在发布前优化模块也可能会阻止应用程序级别的优化。

从 CDN 提供模块时,打包和其他优化可能很有价值,但由于用户可能需要使用依赖 Lit 的多个包,从 CDN 提供服务可能会导致用户加载比必要更多的代码。出于这些原因,我们建议对性能敏感的应用程序始终从 npm 构建,在那里可以去重包,而不是从 CDN 加载打包的包。

如果你想支持从 CDN 使用,我们建议在 CDN 模块和用于生产使用的模块之间做出明确的区分。例如,将它们放在单独的文件夹中,或者只将它们作为 GitHub 发布的一部分添加,而不将它们添加到发布的 npm 模块中。

在导入说明符中包含文件扩展名

Permalink to "在导入说明符中包含文件扩展名"

Node 模块解析不需要文件扩展名,因为如果没有给出扩展名,它会搜索文件系统寻找几个文件扩展名中的一个。当你导入 some-package/foo 时,如果存在 some-package/foo.js,Node 会导入它。同样,在构建时将包说明符解析为 URL 的构建工具也可以进行这种文件系统搜索。

然而,import maps 规范开始在浏览器中发布,它将允许浏览器从源代码_未转换_地加载带有裸包说明符的模块,方法是在导入映射清单中提供导入说明符到 URL 的映射(可能基于你的 npm 安装由工具生成)。

导入映射将允许将导入映射到 URL,但它们只有两种类型的映射:精确和前缀。这意味着通过将包名映射到单个 URL 前缀,可以轻松地别名给定包下的_所有_模块。然而,如果你编写的导入没有文件扩展名,这意味着你的包中的_每个文件_都需要在导入映射中有一个条目。这可能会大大增加导入映射的体积。

因此,为了让你的源代码现在就能最优地兼容导入映射,我们建议在导入时包含文件扩展名。

为了让你的元素易于从 TypeScript 使用,我们建议你:

  • 为所有用 TypeScript 编写的元素添加 HTMLElementTagNameMap 条目。

  • 在你的 npm 包中发布你的 .d.ts 类型定义。

有关 HTMLElementTagNameMap 的更多信息,请参见提供良好的 TypeScript 类型定义

声明 web 组件类的模块应该始终包含一个调用 customElements.define() 的代码(或 @customElement 装饰器)来定义元素。

目前,web 组件总是在全局注册表中定义。每个自定义元素定义都需要使用一个唯一的标签名一个唯一的 JavaScript 类。尝试两次注册相同的标签名,或两次注册相同的类都会失败并报错。简单地导出一个类并期望用户调用 define() 是脆弱的。如果两个不同的组件都依赖于一个共享的第三个组件,并且都试图定义它,其中一个将会失败。如果一个元素总是在声明其类的同一模块中定义,这就不是问题。

这种方法的一个缺点是,如果两个不同的元素使用相同的标签名,它们就不能都导入到同一个项目中。

作用域自定义元素注册表的工作正在平台上进行。作用域注册表允许用户为给定的 shadow root 作用域选择自定义元素的标签名。一旦浏览器开始发布这个功能,为每个组件发布两个模块就会变得实用:一个导出没有副作用的自定义元素类,一个在全局注册表中用标签名注册它。

在此之前,我们建议继续在全局注册表中注册元素。

为了支持子类化,从定义元素的模块中导出你的元素类。这允许出于扩展目的进行子类化,以及在作用域自定义元素注册表中注册。