发布
本页提供了将 Lit 组件发布到 npm 的指南,npm 是大多数 JavaScript 库和开发人员使用的包管理器。有关可用于发布到 npm 的可重用组件模板,请参阅 入门套件。
发布到 npm
“发布到 npm” 的永久链接要将您的组件发布到 npm,请参阅有关贡献 npm 包的说明。
您的 package.json 配置应包含 type
、main
和 module
字段
package.json
{
"type": "module",
"main": "my-element.js",
"module": "my-element.js"
}
您还应该创建一个 README,描述如何使用您的组件。
发布现代 JavaScript
“发布现代 JavaScript” 的永久链接我们建议以标准 ES2021 语法发布 JavaScript 模块,因为所有常青浏览器都支持此语法,并且会导致 JavaScript 更快、更小。您的软件包用户始终可以使用编译器来支持旧版浏览器,但如果在发布之前预编译代码,他们就无法将遗留 JavaScript 转换为现代语法。
但是,重要的是,如果您正在使用新提出的或非标准的 JavaScript 功能(例如 TypeScript、装饰器和类字段),您应该将这些功能编译为标准 ES2021(浏览器本地支持)并在发布到 npm 之前。
使用 TypeScript 编译
“使用 TypeScript 编译” 的永久链接以下 JSON 示例是一个部分 tsconfig.json
,它使用推荐的选项来定位 ES2021,启用装饰器的编译,并为用户输出 .d.ts
类型
tsconfig.json
"compilerOptions": {
"target": "es2021",
"module": "es2015",
"moduleResolution": "node",
"lib": ["es2021", "dom"],
"declaration": true,
"declarationMap": true,
"experimentalDecorators": true,
"useDefineForClassFields": false
}
注意,将 useDefineForClassFields
设置为 false
仅在 target
设置为 es2022
或更高(包括 esnext
)时才需要,但建议明确确保此设置是 false
。
从 TypeScript 编译时,您应该在 package.json
的 types
字段中包含组件类型的声明文件(根据上面的 declaration: true
生成),并确保 .d.ts
和 .d.ts.map
文件也发布。
package.json
{
...
"types": "my-element.d.ts"
}
有关更多信息,请参阅 tsconfig.json 文档。
使用 Babel 编译
“使用 Babel 编译” 的永久链接要编译使用 ES2021 中尚未包含的提出的 JavaScript 功能的 Lit 组件,请使用 Babel。
安装 Babel 和您需要的 Babel 插件。例如
npm install --save-dev \
@babel/core \
@babel/cli \
@babel/preset-env \
@babel/plugin-proposal-decorators
配置 Babel。例如
babel.config.json
{
"presets": [
["@babel/preset-env", {"targets": "defaults"}]
],
"plugins": [
["@babel/plugin-proposal-decorators", {"version": "2023-05"}]
]
}
您可以调整 "targets"
选项以定位您希望支持的浏览器。有关可用选项,请参阅 @babel/preset-env
。
您可以通过捆绑器插件(例如 @rollup/plugin-babel)或从命令行运行 Babel。有关更多信息,请参阅 Babel 文档。
发布最佳实践
“发布最佳实践” 的永久链接以下是在发布可重用 Web 组件时应遵循的其他最佳实践。
不要将 polyfills 导入模块
“不要将 polyfills 导入模块” 的永久链接Polyfills 是应用程序的关注点,因此应用程序应该直接依赖于它们,而不是单个包。所需的具体 polyfills 通常取决于应用程序需要支持的浏览器,而该选择最好留给使用您的组件的应用程序开发人员。您的组件文档应该清楚地识别它使用的任何可能需要 polyfills 的 API。
包可能需要依赖于 polyfills 用于测试和演示,因此如果需要,它们应该只放在 devDependencies
中。
不要捆绑、缩小或优化模块
“不要捆绑、缩小或优化模块” 的永久链接捆绑和其他优化是应用程序的关注点。在发布到 npm 之前捆绑可重用组件还会将 Lit(和其他包)的多个版本引入用户的应用程序,因为 npm 无法对包进行重复数据消除。这会导致膨胀,并可能导致错误。
在发布之前优化模块也可能阻止应用程序级别的优化。
从 CDN 提供模块时,捆绑和其他优化可能很有价值,但由于用户可能需要使用依赖于 Lit 的多个包,因此从 CDN 提供会导致用户加载比必要更多的代码。出于这些原因,我们建议性能敏感的应用程序始终从 npm 构建,在 npm 中可以对包进行重复数据消除,而不是从 CDN 加载捆绑的包。
如果您想支持从 CDN 使用,我们建议在用于 CDN 的模块和用于生产用途的模块之间进行明确的区分。例如,将它们放在单独的文件夹中,或者只将它们作为 GitHub 版本的一部分添加,而不是将它们添加到已发布的 npm 模块中。
在导入说明符中包含文件扩展名
“在导入说明符中包含文件扩展名” 的永久链接Node 模块解析不需要文件扩展名,因为它会执行文件系统的搜索,如果未给出文件扩展名,则查找几个文件扩展名中的一个。当您导入 some-package/foo
时,Node 会导入 some-package/foo.js
(如果存在)。同样,在构建时将包说明符解析为 URL 的构建工具也可以执行此文件系统搜索。
但是,导入映射 规范正在开始在浏览器中发布,它将允许浏览器从源加载具有裸包说明符的模块未经转换,方法是通过在导入映射清单(这很可能基于您的 npm 安装(例如)通过工具生成)中提供导入说明符到 URL 的映射。
导入映射将允许将导入映射到 URL,但它们只有两种类型的映射:精确和前缀。这意味着通过将包名称映射到单个 URL 前缀,可以轻松地为给定包下的所有模块设置别名。但是,如果您在导入中没有文件扩展名,则意味着您的包中的每个文件都需要导入映射中的条目。这会大大膨胀导入映射。
因此,为了让您的源代码现在能够与导入映射最佳兼容,我们建议在导入中使用文件扩展名进行创作。
发布 TypeScript 类型定义
“发布 TypeScript 类型定义” 的永久链接为了让您的元素易于从 TypeScript 中使用,我们建议您
为在 TypeScript 中编写的每个元素添加
HTMLElementTagNameMap
条目。@customElement('my-element')
export class MyElement extends LitElement { /* ... */ }
declare global {
interface HTMLElementTagNameMap {
"my-element": MyElement;
}
}
在您的 npm 包中发布
.d.ts
类型定义。
有关 HTMLElementTagNameMap
的更多信息,请参阅 提供良好的 TypeScript 类型定义。
自定义元素
“自定义元素” 的永久链接声明 Web 组件类的模块应始终包含对 customElements.define()
(或 @customElement
装饰器)的调用来定义元素。
目前,Web 组件始终在全局注册表中定义。每个自定义元素定义都需要使用唯一的标签名称以及唯一的 JavaScript 类。尝试两次注册相同的标签名称或相同的类将导致错误。简单地导出一个类并期望用户调用 define()
是脆弱的。如果两个不同的组件都依赖于同一个共享的第三个组件,并且都尝试定义它,那么其中一个将失败。如果元素始终在声明其类的同一个模块中定义,则这不是问题。
这种方法的一个缺点是,如果两个不同的元素使用相同的标签名称,则它们不能都导入到同一个项目中。
人们正在努力为平台添加 作用域自定义元素注册表。作用域注册表允许组件用户为给定的 shadow root 作用域选择自定义元素的标签名称。一旦浏览器开始发布此功能,将变得切实可行地为每个组件发布两个模块:一个导出没有副作用的自定义元素类,另一个在全局范围内注册它并使用标签名称。
在此之前,我们建议继续在全局注册表中注册元素。
导出元素类
“导出元素类” 的永久链接为了支持子类化,请从定义它的模块中导出元素类。这允许子类化以用于扩展目的,以及将来在 作用域自定义元素注册表 中注册。
更多阅读
“更多阅读” 的永久链接有关创建高质量可重用 Web 组件的更一般指南,请参阅 Web 组件黄金标准清单。