最近,我寫了一篇關於如何構建你的第一個 Web 元件的文章,以及一些關於基本的 v1 Web 元件規範的歷史和解釋。但自 2020 年 v1 獲得完全支持以來,Web 元件的世界發生了更多的變化。未來還有更多的計劃。讓我們看一些使用當前標準構建的值得關注的例子,以及調查一些將在 2023 年及以後推出的新的 Web 元件標準工作1。
Web 元件應用實例#
隨著所有瀏覽器都支持 v1 Web 元件,許多公司已經採用並基於這些新標準構建了重要的業務。以下是我認為值得關注的一些例子。
YouTube#
YouTube 是最早採用 Web 元件技術的應用之一,多年來一直使用這種技術構建其界面。檢查源代碼,你會看到各種自定義元素,從 ytd-video-preview
到 iron-ally-announcer
。
Photoshop#
是的,Adobe 使用 Lit 將 Photoshop 帶到了瀏覽器中。它現在還處於 beta 版本,如果你是 Adobe 的訂閱用戶,可以自行嘗試。整個應用程序中有很多自定義元素,從構成應用程序根的 psw-app
,到像 psw-layers-panel
這樣的 shell 元素,再到像 sp-action-button
這樣的 UI 組件。
MSN, Edge, Bing, VS Code, and More at Microsoft#
幾年前,微軟使用基於 FAST 的 Web 元件重構了 MSN。這將性能提高了 30% 到 50%,比之前使用 React 構建的版本性能更好。
基於 OpenAI 的 New Bing 也是使用 FAST Web 元件 構建的,如下面的屏幕截圖所示,最近由其中一位開發人員分享。
甚至用於擴展 VS Code 新功能的 Webview UI 工具包,也是使用 FAST Web 元件 構建的。
在過去三年中,微軟大約有 1,500 個團隊 / 項目採用了 FAST Web 元件。
Salesforce#
作為客戶關係管理(CRM)、銷售和營銷自動化平台行業中最大的品牌之一,Salesforce 多年來一直在基於 Lightning Web 元件 進行開發。
SpaceX#
如今,Web 元件甚至在太空中也得到了應用。SpaceX 的機組人員顯示屏正在運行 Chromium,廣泛使用 Web 元件。
標準現狀#
Web 標準不斷發展,其中包括 Web 元件。自從 v1 版本發布到所有主流瀏覽器以來的三年中,Web 元件下的功能數量幾乎翻了一倍。以下是各種已發布、正在進行和計劃中的 Web 元件相關標準的圖示。
讓我們逐一查看圖示中按照功能劃分的六個高級類別中的每一項:組合和作用域、平台互操作性、渲染和性能、樣式、包和分發、API 範式。
組合和作用域#
Web 元件的作用域 / 封裝特性對於傳統編程中的信息隱藏、維護、代碼庫可擴展性等方面同樣非常重要。但是,當涉及到 Web 元件時,它們還為 HTML 和 CSS 運行時提供了額外的元數據,可以用來優化繪製和佈局。
Shadow DOM
Shadow DOM 是 HTML 中用於作用域、封裝和組合 DOM 及相關樣式的基本機制。它是一個多方面的特性,具有許多不斷擴展的能力。
-
命名插槽分配(全面支持)—— 原始的 v1 Shadow DOM 規範提供了一種完全聲明式的機制,用於在 Shadow DOM 中使用命名的
<slot>
元素來定義元素組合的佔位符。開發者只需在宿主元素的任何子元素上放置一個slot
屬性,瀏覽器就會自動『插入』該元素的呈現輸出到插槽的位置。 -
開放和封閉模式(全面支持)—— v1 Shadow DOM 規範中的 attachShadow() API 的
mode
選項是其中一部分。它允許組件開發者選擇其首選的封裝模式。open
模式允許從宿主元素外部訪問shadowRoot
,而closed
模式則禁止訪問。 -
事件重新定向(全面支持)—— 當在 Shadow DOM 內部的元素上觸發事件時,這些事件會被『重新定向』,以便它們看起來來自宿主 Shadow DOM。這個 v1 Shadow DOM 規範的能力是正確封裝內部結構的重要部分。
-
手動插槽分配(全面支持)——
slot
元素上的新的 assign API 擴展了 v1 的原始插槽分配功能,除了之前的聲明性式插槽機制外,還提供了一種命令式 API。 -
焦點委託(全面支持)—— 這個在 v1 之後的特性使 Shadow DOM 可以告訴瀏覽器,當其宿主元素獲得焦點時,它應該將焦點委託給 Shadow DOM 中的特定元素。默認情況下,第一個可聚焦的元素被選中,但可以使用
autofocus
屬性覆蓋該行為。 -
Cross-root ARIA(接近共識)—— 即將到來的特性,與社區和瀏覽器廠商接近達成共識,Cross-root ARIA 將極大簡化在 Shadow DOM 外部與 Shadow DOM 內部的 ARIA 關鍵元素相關聯的操作。例如,將 Shadow DOM 外部的
label
元素與 Shadow DOM 內部的input
元素關聯起來。這些類型的 ARIA 場景今天已經有解決方案,但並不是容易實現的。Cross-root ARIA 將大大改善這種情況。 -
在 Shadow CSS 中使用自定義屬性(共識)—— 如今,一些瀏覽器可以使用 @property 語法來自定義 CSS 屬性。但是,目前在 Shadow DOM 中尚不起作用。CSS 對象模型始終可以從自定義元素代碼中來全局定義這些屬性,但在 Shadow DOM 中以聲明形式提供此功能是一個常識性的改進。這已達成共識,因此希望我們很快就能看到這個功能。隨著瀏覽器更普遍地支持新的 CSS 語法。
作用域元素註冊表(共識)
在自定義元素的 v1 規範中,所有元素都通過 customElements
全局對象在全局自定義元素註冊表中註冊。這個新的補充功能使得能夠實例化非全局註冊表並在其中註冊自定義元素。
const myRegistry = new CustomElementRegistry();
myRegistry.define("my-element", MyElement);
這個註冊表中的元素僅定義為該註冊表所分配的 Shadow DOM。這極大地改進了瀏覽器中的作用域,使得可以按照需要為每個 shadow root 定義元素。當它被應用到瀏覽器中時,這將會是個巨大的進步,為新的架構可能性打開了大門。目前,社區和廠商之間已經達成共識,Chromium 正在開發第一個實現。
平台互操作性#
Web 元件最重要的方面之一是它們如何在組件和平台之間實現互操作性。讓我們看一些當前和未來的特性。
自定義元素
-
自治自定義元素(全面支持)—— Web 元件 v1 的這個核心功能通過向
customElements
全局對象註冊一個類來定義繼承自HTMLElement
的自定義元素。基本的生命週期回調和觀察屬性也是規範的一部分。 -
自定義內置元素(已拒絕)—— 最初,有一個提案允許從內置元素(例如
HTMLParagraphElement
)繼承,但 WebKit 實現者發現了幾個技術問題,因此已經拒絕了這個規範。它很可能在將來被刪除,所以應該避免使用。請參見下面的『自定義屬性』,了解可能更好的替代方案。
Element Internals
一個 v1 之後的新 API,ElementInternals
,使得自定義元素能夠更深入地與現有的 DOM 子系統進行平台級集成。
-
Shadow Root 訪問(全面支持) —— 這個簡單的功能添加使組件開發者可以檢索一個
closed
模式元素的 Shadow Root 實例。如果沒有這個功能,具有closed
模式聲明式 Shadow DOM 的元素將無法在運行時訪問其根節點。 -
與表單關聯的自定義元素(全面支持) —— 這個重要的新功能使得自定義元素能夠完全參與表單,包括表單驗證、提交和重置。
-
默認可訪問性角色、狀態和屬性(大多數已支持) —— 這個新的 API 集合 使得可以通過直接在內部與平台進行通信來設置自定義元素的默認可訪問性特性,而不是通過可能被用戶無意中刪除的宿主元素上的外部屬性。目前,除 Firefox 外的所有主要瀏覽器都支持這個新的 API,對於 Firefox,則提供了一個 polyfill。由於 Firefox 已經實現了
ElementInternals
其他部分的 API,如果他們在不久的將來沒有發布這個功能,我會感到驚訝。
組合選擇(共識 / 無規範)
這個改進提出了一個新的 getComposedRange()
API,用於 Selection 對象,它使得範圍的起始和結束可以跨越多個 Shadow Root。它還將提升瀏覽器在處理這些情況時的一致性。對於 這個 API 草案,有普遍的共識,但在瀏覽器可以進行實現之前,仍需要一個完整的規範。在 Web Component 的正常開發過程中,你不太可能遇到這種情況。它主要涉及到富文本編輯器的實現。
自定義屬性(已確定)
雖然這個功能不一定是 Web 元件的一部分,但它與 Web 元件旨在服務的場景有很高的重疊。這個草案提議啟用可重用行為的創建,這些行為可以附加到任何 HTML 元素,遵循類似於 Web 元件的模式。例如,想像一下你想將 Material Design 水波紋效果應用到任何 HTML 元素上,那麼這樣做會不會很好呢?
<button material-ripple>Click Me</button>
在我為 TPAC 2022 準備的草案提案中,我演示了這個功能的編程模型可能是什麼樣的:
class MaterialRipple extends Attr {
// ownerElement inherited from Attr
// name inherited from Attr
// value inherited from Attr
// ...
connectedCallback () {
// called when the ownerElement is connected to the DOM
// or when the attribute is added to an already connected owner
}
disconnectedCallback () {
// called when the ownerElement is disconnected from the DOM
// or when the attribute is removed from a connected owner
}
attributeChangedCallback() {
// called when the value property of this attribute changes
}
}
customAttributes.define("material-ripple", MaterialRipple);
你會注意到,這個模式和生命週期與 Web 元件是一致的。這也將為被拒絕的可定制內置自定義元素提案中的 is
屬性提供更好、更健壯的替代方案。
渲染和性能#
渲染和性能對於 Web 元件來說非常關鍵。雖然基本功能已經就位,但這仍然是一個活躍的探索、討論和未來創新的領域。
HTML Template 元素(全面支持)
HTMLTemplateElement
及其定義惰性 HTML 內容的能力是 v1 Web 元件功能的關鍵部分。在引入該元素之前,沒有辦法聲明不會被瀏覽器激活的 HTML,因此很難創建需要在需求時重複渲染相同 HTML 的組件。
聲明式 Shadow DOM(大多數支持)
Shadow DOM 的 v1 規範僅允許通過 attachShadow()
JavaScript API 創建 Shadow Root。這個 Shadow DOM 的新增增強功能允許在 HTML 中完全聲明 Shadow DOM 內容,無需使用 JavaScript,為伺服器框架提供了有趣的可能性。
<host-element>
<template shadowrootmode="open">
<slot></slot>
</template>
<h2>Light content</h2>>
</host-element>
這個規範重用了 template
元素。不要被這個搞混了。它不是一個模板,它是由 HTML 解析器流入 Shadow Root 的活動 DOM。
當前除了 Firefox 之外,所有瀏覽器都支持聲明式 Shadow DOM。 如果需要,該功能可以通過幾行 JavaScript 代碼進行 polyfill。
子節點更改回調函數(提議)
Web 元件在自定義元素的 v1 規範中有一個明確定義的生命週期,但這並不意味著我們不能在未來擴展這個生命週期。其中一個常見的對於開發者的挑戰是使 Web Component 能夠对子節點的添加或刪除做出響應。雖然現在可以使用 slotchange 事件 和 MutationObserver 實現這一點,但是如果有一個像 childrenChangedCallback()
這樣的生命週期回調函數,可以提供更好的性能、簡化和與 HTML 解析器本身的集成,那就更好了。目前有一個草案提議,並且實現者也表現出了興趣。需要一份完整的提案來推動這個功能進入下一個階段。
模板實例化
雖然 HTML 有模板,但它還沒有一種機制來實例化與數據連接的模板,並在其相關數據更改時更新它們。這個『模板實例化』的領域有幾個獨立有價值的部分。
- DOM 部件(提議) - 這個提案 將提供一種標準機制,在 DOM 樹的特定位置插入或替換內容。你可以把它看作是一種低級別的啟用器,幫助創建更高效的模板引擎和批量更新現有的 Web Component 庫和 JavaScript 框架。它不提供響應性解決方案或模板語法,只提供定位和更新 DOM 部分的低級別標準基礎設施。
- 模板語法(已確定) - 一旦定位和批量更新的低級別基礎設施就位並被現有庫成功使用,那麼關於語法的大辯論就會開始。模板語法是一個非常有争议的问题,但我們已經認識到 HTML 應該有一個基本的語言來處理這個問題,即使它只是為其他庫提供編譯目標。
- 響應性(已確定) - DOM 部件提供批量更新 DOM 的標準機制。模板語法提供聲明式機制來創建 DOM 部分。剩下的是確定何時應執行 DOM 部件更新的機制。這就是響應性的作用,以完成整個圖景。這是另一個有爭議的問題,但已經有一些先例,例如通過 Web 元件的
attributeChangedCallback()
。這個主題需要更多的探索。
模板實例化工作類別被分解為上述三個子特性,旨在先解決某些較少有爭議的問題,並為現有庫和框架提供路徑,以利用不那麼主觀的、改進性能的功能,避免在社區中引起過多爭議。
樣式#
雖然 Shadow DOM 提供了樣式的封裝,但有許多 CSS 特性直接與 Web 元件相關,並且在日常使用中非常重要。
使用
有幾項當前和未來的標準與 Web 元件如何使用樣式來創建 Shadow DOM 的呈現方式有關。雖然一直以來都可以在 Shadow DOM 中創建樣式元素,但新標準提供了更好的可讀性和性能優勢。
- 可構建樣式表(全面支持) — 你知道在這個標準之前實際上無法創建
CSSStyleSheet
實例嗎?這個標準修復了這個問題,現在您可以編寫代碼new CSSStyleSheet()
。這種能力使得在 Web 元件中更動態地創建和使用樣式成為可能,包括在組件之間共享樣式表。 - 採用樣式表(全面支持) — 針對給定的
CSSStyleSheet
實例,如何將其與特定的 Shadow Root 或全局 document 關聯起來?這個新標準 在document
和所有 Shadow Root 實例中添加了一個adoptedStyleSheets
陣列。只需將樣式表推入該陣列中,就可以開始使用了。 - CSS 模塊腳本(Chromium) — 可構建樣式表和採用樣式表本身提供了創建、共享和關聯文檔表的原始機制,但仍需要在 JavaScript 中編寫 CSS 代碼。CSS 模塊腳本標準 允許使用 JavaScript 模塊導入
.css
文件,從而平台會自動創建一個CSSStyleSheet
實例,無需在 CSS 運行時和 JavaScript 運行時之間來回切換。 - 聲明式 CSS 模塊(已確定) — 隨著聲明式 Shadow DOM 和採用樣式表的出現,已經創建了幾個臨時提議,以便聲明 CSS 模塊並將其與聲明式 Shadow DOM 關聯。這方面需要更多的探索,但這是 HTML 和 CSS 未來的一個令人興奮的可能性。
呈現
主要來說,CSS 關注的是呈現方面的問題。有一些標準擴展了 Web 元件的樣式設置的可能性。
不僅僅是 Web 元件,對於創建組件系統來說,自定義 CSS 屬性 是一個非常重要的規範,它能夠創建本地 CSS 變量,並可以在 shadow roots 中使用。
- CSS Shadow Parts(全面支持) — CSS 部分 允許在 Shadow DOM 中聲明元素作為『部分』,可以使用外部選擇器對其進行樣式設置。這是通過
part
屬性和 the exportparts 屬性 實現的,用於嵌套場景。 - CSS 自定義狀態(Chromium) — 原生元素可以具有自定義狀態,在 CSS 選擇器中可用。例如,複選框的『已選中』和『未選中』狀態。這個新功能 允許 Web 元件定義自己的狀態。已經達成共識,Chromium 已經發布了第一個實現,可以通過
ElementInternals
訪問。在等待其他瀏覽器跟進時,可以使用 polyfill 進行支持。 - CSS 主題(提議) — 儘管可以通過仔細使用 CSS 自定義屬性和 CSS Shadow Parts 來實現豐富的主題化,但可以通過明確地將主題的概念引入 CSS來簡化和改進這一過程。
- 開放式 Shadow Root 樣式(已確定) — 儘管可以使用可構建樣式表和採用樣式表使任何全局 CSS 在 Shadow Root 中共享,但對於普通 Web 開發人員來說,這可能不是一個直觀的過程。有一些探索機制的方法,明確選擇允許外部 CSS 進入某些 shadow roots。
打包和分發#
到目前為止,我們主要談論了與 Web 元件實現相關的標準。但同樣重要的是考慮組件如何打包和加載。
自定義元素懶加載(提議)
現在,我們可以使用全局的 customElements
註冊表定義組件,很快還可以使用自定義註冊表。但是,在這兩種情況下,在定義組件之前,組件的實現必須已經被加載。使用自定義元素懶加載,開發人員將能夠告訴平台有關元素的信息,但是可以延遲加載它,直到元素首次出現在 document 中時才加載。它可能會像這樣工作:
customElements.defineLazy(
"my-element",
async () => (await import("my-element.js")).default
);
這個規範似乎被大多數人認為是一種很好的東西,尤其是對於某些架構來說。然而,該提議的細節仍在爭論中。
HTML 模塊腳本(已確定)
HTML 模塊腳本是 CSS 模塊腳本的 HTML 等效物。通過 HTML 模塊腳本提案,模板(和其他 HTML 片段)將通過 JS 模塊系統直接可導入。目前只有一個草案提案,還需要進一步討論許多細節,但這被認為是一個重要的長期增強功能,特別是考慮到未來可能存在只有單個 HTML 文件的 Web 元件的情況。
API 範式#
最後一個標準類別與我之前討論的所有內容有些不同。這些標準涉及 Web 元件的基本編程範式。Web 元件 v1 主要是一種命令式的 JavaScript 編程模型。有一些值得注意的例外,比如聲明式的 slot 分配。但基本上,它完全是命令式的。自 v1 以來,我們一直在努力引入越來越多的聲明式特性。其中一個很好的例子是聲明式 Shadow DOM。總的來說,最好為所有場景提供聲明式和命令式的 API。但最終目標是擁有某種完全聲明式定義的 Web 元件,以便伺服器可以向瀏覽器發送元素定義,在 noscript 上下文中可以完全工作。我們還有一段路要走,但當我們到達那裡時,它將從根本上改變客戶端和伺服器開發。
下一步是什麼#
標準的工作永遠在進行中。事實上,從今天開始,W3C Web 元件社區組正在召開其 2023 年春季面對面活動。就像 TPAC 一樣,這是一個機會,供庫作者、組件創建者、瀏覽器廠商等聚集在一起,並花費專門的時間來解決仍需要共識或存在開放問題的規範的細節。我期待在後續的博客文章中向大家更新活動結果。請關注 / 訂閱以確保您獲得更新 😄
總結#
我希望本次的 Web 元件標準之旅對你來說是有意義的。你看到我們已經走了多遠,未來還有什麼等待我們,這很有趣。隨著 v1 版本的發布,過去幾年中已經發布的功能翻了一番,以及即將到來令人興奮的新功能,現在是成為 Web 開發人員的好時機。