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,這可能會是重大變更。
在 Webpack 4 中使用 Lit 3
「在 Webpack 4 中使用 Lit 3」的永久連結Webpack 4 的內部剖析器不支援 nullish coalescing (??
)、邏輯指派 (??=
) 或選用鏈結 (?.
),這些語法是在 ES2021 中引入的,因此當遇到這些語法時會擲回 Module parse failed: Unexpected token
錯誤。
較好的解決方案是升級至 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 標準化,並且處於四階段標準化程序的第三階段。第三階段是 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 專案沒有棄用警告,您應該不會受到此清單的影響。
- 已移除
ReactiveElement
的UpdatingElement
別名。 - 已移除從主要
lit-element
模組重新匯出裝飾器。 - 已移除已棄用的
queryAssignedNodes
裝飾器呼叫簽章。 - 已將實驗性伺服器端渲染水合模組從
lit
、lit-element
和lit-html
移至@lit-labs/ssr-client
。
升級步驟
「升級步驟」的永久連結已移除 ReactiveElement
的 UpdatingElement
別名
「移除 ReactiveElement 的 UpdatingElement 別名」的永久連結 將 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 7.23 和 @babel/plugin-proposal-decorators
外掛程式。
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;
}
}