发布
本页面提供了将 Lit 组件发布到 npm 的指南,npm 是绝大多数 JavaScript 库和开发者使用的包管理器。查看入门套件了解设置好用于发布到 npm 的可重用组件模板。
发布到 npm
Permalink to "发布到 npm"要将你的组件发布到 npm,请参见贡献 npm 包的说明。
你的 package.json 配置应该有 type
、main
和 module
字段:
package.json
{
"type": "module",
"main": "my-element.js",
"module": "my-element.js"
}
你还应该创建一个 README,描述如何使用你的组件。
发布现代 JavaScript
Permalink to "发布现代 JavaScript"我们建议以标准 ES2021 语法发布 JavaScript 模块,因为这在所有常青浏览器上都受支持,并且能产生最快和最小的 JavaScript。你的包的使用者可以随时使用编译器来支持旧版浏览器,但如果你在发布前预编译代码,他们就无法将旧版 JavaScript 转换为现代语法。
然而,重要的是,如果你使用新提出的或非标准的 JavaScript 特性,如 TypeScript、装饰器和类字段,你_应该_在发布到 npm 之前将这些特性编译为浏览器原生支持的标准 ES2021。
使用 TypeScript 编译
Permalink to "使用 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 编译
Permalink to "使用 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 文档。
发布最佳实践
Permalink to "发布最佳实践"以下是发布可重用 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 类型定义
Permalink to "发布 TypeScript 类型定义"为了让你的元素易于从 TypeScript 使用,我们建议你:
为所有用 TypeScript 编写的元素添加
HTMLElementTagNameMap
条目。@customElement('my-element')
export class MyElement extends LitElement { /* ... */ }
declare global {
interface HTMLElementTagNameMap {
"my-element": MyElement;
}
}
在你的 npm 包中发布你的
.d.ts
类型定义。
有关 HTMLElementTagNameMap
的更多信息,请参见提供良好的 TypeScript 类型定义。
自定义元素
Permalink to "自定义元素"声明 web 组件类的模块应该始终包含一个调用 customElements.define()
的代码(或 @customElement
装饰器)来定义元素。
目前,web 组件总是在全局注册表中定义。每个自定义元素定义都需要使用一个唯一的标签名和一个唯一的 JavaScript 类。尝试两次注册相同的标签名,或两次注册相同的类都会失败并报错。简单地导出一个类并期望用户调用 define()
是脆弱的。如果两个不同的组件都依赖于一个共享的第三个组件,并且都试图定义它,其中一个将会失败。如果一个元素总是在声明其类的同一模块中定义,这就不是问题。
这种方法的一个缺点是,如果两个不同的元素使用相同的标签名,它们就不能都导入到同一个项目中。
作用域自定义元素注册表的工作正在平台上进行。作用域注册表允许用户为给定的 shadow root 作用域选择自定义元素的标签名。一旦浏览器开始发布这个功能,为每个组件发布两个模块就会变得实用:一个导出没有副作用的自定义元素类,一个在全局注册表中用标签名注册它。
在此之前,我们建议继续在全局注册表中注册元素。
导出元素类
Permalink to "导出元素类"为了支持子类化,从定义元素的模块中导出你的元素类。这允许出于扩展目的进行子类化,以及在作用域自定义元素注册表中注册。