Lit 3 升级指南
如果您要从 Lit 1.x 迁移到 Lit 2.x,请参阅 Lit 2 升级指南。
Lit 3.0 与 Lit 2.x 的区别非常小
- 不再支持 IE11。
- Lit 的 npm 模块现在以 ES2021 发布。
- 在 Lit 2.x 版本中标记为已弃用的 API 已被删除。
- SSR 水合支持模块已移至
@lit-labs/ssr-client
包。 - 类型仅限:
ReactiveElement
的renderRoot
和createRenderRoot()
的类型已更新。 - 已删除对 Babel 装饰器版本“2018-09”的支持。
- 装饰器行为在 TypeScript 实验性装饰器和标准装饰器之间已统一。
- 因此,如果您使用 TypeScript,则需要升级到至少 TypeScript v5.2,才能获得两种装饰器的更新类型。
对于绝大多数用户,从 Lit 2 升级到 Lit 3 不需要进行任何代码更改。大多数应用程序和库应该能够扩展其 npm 版本范围以包含 2.x 和 3.x,例如 "^2.7.0 || ^3.0.0"
。
Lit 2.x 和 3.0 是可互操作的 – 来自 Lit 的一个版本的模板、基类和指令将与来自另一个版本的模板、基类和指令一起使用。
Lit 现在以 ES2021 发布
永久链接到“Lit 现在以 ES2021 发布”Lit 2 以 ES2019 发布,Lit 3 现在以 ES2021 发布,ES2021 在现代浏览器和构建工具中得到广泛支持。如果您需要支持旧版浏览器版本,并且您当前的工具无法解析 ES2021,这可能是重大更改。
在 Webpack 4 中使用 Lit 3
永久链接到“在 Webpack 4 中使用 Lit 3”Webpack 4 的内部解析器不支持空值合并 (??
)、逻辑赋值 (??=
) 或可选链 (?.
),这些语法是 ES2021 中引入的,因此在遇到这些语法时会抛出 Module parse failed: Unexpected token
错误。
首选的解决方案是升级到 Webpack 5,Webpack 5 支持解析这些较新的 JS 语法。但是,如果您无法这样做,则可以使用 babel-loader
将 Lit 3 代码转换为与 Webpack 4 兼容。
要在 Webpack 4 中转译 Lit 3,请安装以下必需的 babel 包
> npm i -D babel-loader@8 \
@babel/plugin-transform-optional-chaining \
@babel/plugin-transform-nullish-coalescing-operator \
@babel/plugin-transform-logical-assignment-operators
并添加一个新的规则,类似于以下规则(您可能需要根据您的具体项目对其进行修改)
// In webpack.config.js
module.exports = {
// ...
module: {
rules: [
// ... your other rules
// Add a babel-loader rule to downlevel Lit's ES2021 syntax so Webpack 4 can parse it.
// TODO: Once on Webpack 5, this rule can be deleted.
{
test: /\.js$/,
include: ['@lit', 'lit-element', 'lit-html'].map((p) =>
path.resolve(__dirname, 'node_modules/' + p)
),
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-optional-chaining',
'@babel/plugin-transform-nullish-coalescing-operator',
'@babel/plugin-transform-logical-assignment-operators'
],
},
},
},
],
}
}
Lit 装饰器的更新
永久链接到“Lit 装饰器的更新”JavaScript 装饰器最近由 TC39 标准化,并且处于四个阶段标准化过程中的第 3 阶段。第 3 阶段是指 JavaScript 实现(例如 VM 和编译器)开始实施稳定规范的阶段。TypeScript 5.2 和 Babel 7.23 最近实施了该标准。
这意味着装饰器 API 存在多个版本:标准装饰器、TypeScript 的实验性装饰器以及 Babel 已实施的以前建议,例如版本“2018-09”。
虽然 Lit 2 支持 TypeScript 实验性装饰器和 Babel 的“2018-09”装饰器,但 Lit 3 现在支持标准装饰器和 TypeScript 实验性装饰器。
Lit 3 装饰器与 Lit 2 TypeScript 装饰器大多向后兼容 - 很可能不需要进行任何更改。
为使 Lit 装饰器在实验性装饰器模式和标准装饰器模式下都能一致地运行,必须进行一些小的重大更改。
Lit 3.0 中 Lit 装饰器行为的更改
requestUpdate()
会自动为@property()
和@state()
装饰的访问器调用,而以前是 setter 的责任。- 访问器的值在第一次渲染时读取,并用作
changedProperties
和属性反射的初始值。 - Lit 3 装饰器不再支持
@babel/plugin-proposal-decorators
的version: "2018-09"
选项。Babel 用户应该 迁移到标准装饰器。 - [可选]:我们建议将
@property()
和@state()
迁移到手动编写的访问器的 setter,以帮助迁移到标准装饰器。
已删除 API 列表
永久链接到“已删除 API 列表”如果您的 Lit 2.x 项目没有弃用警告,则您应该不会受到此列表的影响。
- 已删除 UpdatingElement 对 ReactiveElement 的别名。
- 已删除从主
lit-element
模块中重新导出的装饰器。 - 已删除
queryAssignedNodes
装饰器的已弃用调用签名。 - 已将实验性服务器端渲染水合模块从
lit
、lit-element
和lit-html
移动到@lit-labs/ssr-client
。
升级步骤
永久链接到“升级步骤”已删除 UpdatingElement 对 ReactiveElement 的别名
永久链接到“已删除 UpdatingElement 对 ReactiveElement 的别名”将 Lit 2.x 中对 UpdatingElement
的使用替换为 ReactiveElement
。这不是功能上的更改,因为 UpdatingElement
是 ReactiveElement
的别名。
// Removed
import {UpdatingElement} from 'lit';
// Updated
import {ReactiveElement} from 'lit';
已删除从 lit-element 中重新导出的装饰器
永久链接到“已删除从 lit-element 中重新导出的装饰器”Lit 3.0 内置装饰器 不再由 lit-element
导出,而应该从 lit/decorators.js
中导入。
// Removed decorator exports from lit-element
import {customElement, property, state} from 'lit-element';
// Updated
import {customElement, property, state} from 'lit/decorators.js';
已弃用的 queryAssignedNodes(slot: string, flatten: bool, selector: string)
装饰器签名已删除
永久链接到“已弃用的 queryAssignedNodes(slot: string, flatten: bool, selector: string) 装饰器签名已删除” 迁移任何使用带选择器的 queryAssignedNodes
的代码以使用 queryAssignedElements
。
// Removed
@queryAssignedNodes('list', true, '.item')
// Updated
@queryAssignedElements({slot: 'list', flatten: true, selector: '.item'})
没有 selector
的用法现在必须使用选项对象。
// Removed
@queryAssignedNodes('list', true)
// Updated
@queryAssignedNodes({slot: 'list', flatten: true})
已从 lit、lit-element 和 lit-html 中删除服务器端渲染实验性水合模块
永久链接到“已从 lit、lit-element 和 lit-html 中删除服务器端渲染实验性水合模块”实验性水合支持已从核心库中移出,并移至 @lit-labs/ssr-client
。
// Removed
import 'lit/experimental-hydrate-support.js';
import {hydrate} from 'lit/experimental-hydrate.js';
// Updated
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';
import {hydrate} from '@lit-labs/ssr-client';
[类型仅限]: renderRoot 和 createRenderRoot() 的类型已更新
永久链接到“[类型仅限]: renderRoot 和 createRenderRoot() 的类型已更新”这只是类型更改,对运行时没有影响。
ReactiveElement.renderRoot
的类型已从 Element | ShadowRoot
更改为 HTMLElement | DocumentFragment
,ReactiveElement.createRenderRoot()
的返回类型已从 HTMLElement | ShadowRoot
更改为 HTMLElement | DocumentFragment
。这使得它们彼此一致,也与 lit-html 的 render()
一致。
此更改通常不会影响仅访问 this.renderRoot
的代码。但是,任何对以前类型进行明确类型注解的代码都应该更新。
可选:升级到标准装饰器
永久链接到“可选:升级到标准装饰器”虽然 Lit 3 添加了对标准装饰器的支持,但我们仍然建议 TypeScript 用户坚持使用实验性装饰器。这是因为目前 TypeScript 和 Babel 编译器为标准装饰器发出的代码非常庞大。
当浏览器支持标准装饰器或我们在新的 Lit 编译器中发布装饰器转换支持时,我们会建议使用标准装饰器进行生产。
但是,您现在可以尝试使用标准装饰器,它们在 TypeScript 5.2 及更高版本以及使用 @babel/plugin-proposal-decorators
插件的 Babel 7.23 中有效。
TypeScript
永久链接到“TypeScript”安装 TypeScript 5.2 或更高版本,并删除 tsconfig 中的 "experimentalDecorators"
设置(如果存在)。
Babel
永久链接到“Babel”安装 Babel 7.23 或更高版本,以及 @babel/plugin-proposal-decorators
。务必将 "version": "2023-05"
选项传递给插件。
代码更改
永久链接到“代码更改”将 accessor
关键字添加到装饰的字段
永久链接到“将 accessor 关键字添加到装饰的字段” 标准装饰器不允许更改它们修饰的类成员的类型。需要创建 getter 和 setter 的装饰器必须应用于现有的 getter 和 setter。为了使这更符合人体工程学,装饰器标准添加了accessor
关键字,它在应用于类字段时创建“自动访问器”。自动访问器看起来和行为与类字段非常相似,但会创建由私有存储支持的原型上的访问器。
@property()
、@state()
、@query()
、@queryAll()
、@queryAssignedElements()
和 @queryAssignedNode()
装饰器需要accessor
关键字。
示例
class MyElement extends LitElement {
@property()
accessor myProperty = "initial value"
...
}
将装饰器从 getter 移动到 setter
指向“将装饰器从 getter 移动到 setter”的永久链接标准装饰器只能替换直接应用于它们的类成员。Lit 装饰器需要拦截属性设置,因此装饰器必须应用于 setter。这与 Lit 2 中将装饰器应用于 getter 的建议不同。
对于@property()
和 @state()
,您还可以删除 setter 中的任何this.requestUpdate()
调用,因为现在会自动完成此操作。如果您需要不调用requestUpdate()
,则必须使用noAccessor
属性选项。
请注意,对于@property()
和 @state()
,当设置属性以检索旧值时,装饰器将调用getter。这意味着您必须定义 getter 和 setter。
之前
class MyElement extends LitElement {
private _foo = 42;
set(v) {
const oldValue = this._foo;
this._foo = v;
this.requestUpdate('foo', oldValue);
}
@property()
get() {
return this._foo;
}
}
之后
class MyElement extends LitElement {
private _foo = 42;
@property()
set(v) {
this._foo = v;
}
get() {
return this._foo;
}
}