阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

https://dev.to/ryansolid/maybe-web-components-are-not-the-future-hfh

這是一篇柔和的看法,探討了 Web Components 在何種情況下有意義,在哪些方面又會產生問題。這並不是一場「我們與他們」的辯論,我希望人們能夠自行得出合理的結論。

但在過去幾年中,我只看到這個情況變得更糟。有些人可能從未認真看待 Web Components,但我一直都這樣做。我在生產環境中使用它們已經 7 年了。在早期,我寫了幾個 polyfill,以便在 Shadow DOM 等技術正式推出之前就能使用它們。

但我當時的經驗和此後的經驗只指向一個結論:Web Components 可能對網路的未來構成了最大的風險。


理想的願景

Image description

我承認這句話聽起來有點過於激烈。但在我的背後有許多原因,這就是我相信這一點的原因。這始於理解 Web Components 所提出的好處。

Web Components 的願景是:總有一天,無論你使用什麼工具來創建它們,你都可以擁有像原生 DOM 元素一樣可以添加到任何網站的組件,而不必考慮網站的創建方式。對於特定的構建工具、特定的運行時機制或以任何方式與這些組件交互的方式,都不會有任何變化。

在某種意義上,這是一個可攜式、可互操作的網絡,可以減少未來的遷移需求。這為任何可能的未來鋪平了道路。這是保護你的網站和應用程式未來的完美方法。

這不是一個引人入勝的前景嗎?這正是 Web Components 的承諾如此吸引人且危險的原因。


競爭標準

Image description

我不需要拉出舊的 xkcd 漫畫來讓你知道標準面臨的挑戰。標準越雄心勃勃,就越可能遇到反對意見或替代方法來實現它。這種情況不會在某項技術標準化後消失。這只是表明存在一個被認可的方法,你可以接受也可以拒絕。

如果 JavaScript 框架的數量是任何指標,我們距離達成一個關於如何在網絡上創建組件的共識還遠得很。即使我們今天稍微接近共識,但在十年前我們是遠遠達不到的。

引入更高層次的原語可以產生積極的影響。突然之間,某些原本難以實現的事情變得容易。這最初會導致更多的探索。在2010年代中期,Web Components 導致了 JavaScript 框架數量的增加。這也是我創建 SolidJS 的重要靈感來源。類似的例子是由於 Vite 而興起的 Metaframeworks。

但這也會產生消極影響。如果假設太多,探索替代空間將變得更加困難,因為所有事物都圍繞著現有的建立向心。什麼比一個永遠不會改變的網絡標準更建立?

我在網絡標準之外與這一點掙扎了很多。根據規範,JSX 並沒有明確的語義,但試著說服各種工具不會太過假設,我只能想像如果 JSX 在瀏覽器中被標準化會是多麼可怕的噩夢。更不用說像 Inferno、Solid 和 Million 這樣的框架在他們的 JSX 轉換中做了多麼更優化的事情,即使 React 隨著時間推移也改變了他們的轉換。

這不過是眾多例子之一。幫助我們的事情,能有效地束縛我們的手腳。在標準化任何更高層次的機制時,我們必須小心翼翼,因為這假設了很多。說不需要每個人都使用它是不夠的,因為它的存在影響了我們對整個平台的看法。


機會成本是真實的

作為一名框架作者,我對此理解得非常透徹。我經常說,在這個領域,事情既是被發現的,也是被發明的。我的意思是,設計決策背後有某種真理/物理學,如果遵循這些原則會將我們引導到相似的地方。這並不是說這些工具明顯地互相抄襲。它們都會進入某種模式。

而出於相同的原因,一旦發現改變了我們的觀點,損害在我們編寫第一行代碼之前就已經造成了。如果你的工作是設計系統,你不希望有冗餘的部分。你希望有明確且有目的的邊界。你試著重用同一件事,而不是對相同的事物做出一百萬種變化。更加認識到,為了完成常見任務,你需要多個元素,這些部分便變得交織在一起。

例如,React 開發者明確感受到了自 2017 年宣布 Suspense 到 2022 年我們最終得到了 RSC 的數據獲取故事之間有多長。為什麼會這樣耗時 5 年?因為這並不是一條直線。我們花了一段時間才理解所有的部分如何配合在一起。這本身是合理的。但更重要的是,React 不想逐步釋放這些,直到知道它們擁有完整故事的解決方案。隨著他們不斷探索,所有這些組件都是相關的,雖然可以拆分,但彼此需要來繪製整體圖景。

RSCs 並不符合每個人對 React 中使用 Suspense 進行數據獲取的想法。也許人們會從客戶端數據獲取原語中受益。React 在這方面選擇了雄心勃勃,這是他們作為工具的權利並且決定了最佳方案,但這本可以以多種方式實現。

作為一名開發者,我永遠可以選擇不使用 React。而雖然我可以選擇不使用某些 React 功能,但顯然 React 的所有內容都已轉向他們當前的思維模式。我甚至可能希望我能輕鬆地從 React 遷移。

但這裡有一個很大的區別。React 是一個庫,而不是一個標準。當談到標準時,這些選擇並不相同。如果我只想要範圍樣式,而現在我必須處理 Shadow DOM,因為這是最適合將一切牢牢綁定在一起的抽象,那麼我就被困在這裡。

當原語超出了其期望的使用範圍,當它們過於抽象時,你無法從中回來。你已經支付了代價。正如任何曾經對項目進行過重大架構重構的人所證明的,最難的部分是調整邊界。如果所有事物都屬於相同的邏輯分組,那麼它們更容易更新。當你需要拆分事物時,這就真正具有挑戰性。你總是可以添加另一層抽象來解決問題,但移除一層則可能非常困難。


抽象的代價

Image description

因此,Web Components 的根本問題在於它們是基於自定義元素建構的。元素並不等同於組件。更具體地說,元素只是組件的一個子集。可以說,每個元素都可以是組件,但並非所有組件都是元素。

那又怎樣?這意味著每個介面都需要通過 DOM。一些以明確的方式進行,並不完美匹配,還有一些以新定義的方式進行,擴展或改變了與元素處理的方式,以適應增強的功能。

首先,DOM 元素有屬性和屬性。這樣它們可以以 HTML 的形式表示。屬性只接受字串,而屬性作為 JS 接口可以處理任何值。原生 DOM 元素有許多關於特定屬性/屬性的規則,比如某些是布林值(存在意味著它們適用),而其他則是偽布林值(需要明確的「真」或「假」)。某些屬性會反映到屬性中,其他則不會。

模板語言的一個目標是以統一的方式解決這個問題。我們可以圍繞已知的元素和屬性制定特殊規則。但對於自定義元素,我們卻不知道。因此這就是為什麼一些模板庫有有趣的前綴來指示如何設置事物。即使在 Solid 的 JSX 中,我們也有 attr:prop:bool: 前綴。現在每個運行時位置和編譯器鉤子都需要注意這一點。

你可能會想,我們需要更好的模板作為標準。但是就像上述 JSX 一樣,你需要考慮這一決定的影響。幾年前,大多數人可能會同意像 LitHTML 這樣的模板渲染方式是一種好方法。其他解決方案也可以輸出它。然而,在這期間,我們意識到,像 Solid 這樣的反應式渲染要比這個結果更好。它是通過改變模板的語義實現的。如果我們當初繼續推進,我們將擁有一個不是最佳模板化方式的標準。

事情並不止於此。DOM 元素可以被克隆。但自定義元素有不同的行為,這意味著它們應該被導入。它們擁有基於 DOM 的生命週期,可以根據升級的時間以同步或異步的方式觸發。這對於像反應性追蹤和上下文 API 的事情造成了混亂。然而,這些細節對於對原生 DOM 行為和事件進行接口設計至關重要。這些都是 JavaScript 組件不需要擔心的事情。

還有其他特例,比如在 Shadow DOM 中事件目標的工作方式。有些事件不會「組合」,即不在 Shadow DOM 邊界之外冒泡。有些事件不一致地冒泡,因為它們不識別不同的目標,比如在「focusin」中,因為 Shadow Host 會始終使其成為目標,無論哪個子元素獲得焦點。我們可以談論這些問題好幾天,但我不想在這裡偏離太多。有些是當前的不足之處,有些是設計使然。但這裡的共性是,它們都需要特定的考慮,否則就不會是必需的。

當然,這也帶來了性能開銷:
https://dev.to/this-is-learning/the-real-cost-of-ui-components-revisited-4d23

但即使你認為這個性能成本微小,當涉及伺服器和 SSR 等問題時,我們又該如何應對呢?是的,你可以完全對 Web Components 做 SSR。水合是完全可以實現的。但是它們是在沒有 DOM 的地方的 DOM 接口。你可以創建一個最小的包裝器來處理大多數事情,但這都是額外的開銷。全是因為我們試圖將組件變成它們本來並不是的東西。

在伺服器上並沒有這樣的標準。我們又回到了具體解決方案的情況。這只是一種不同類型的框架,沒有比我選擇 Vue 或 React 更多的保證。這並沒有錯,但我們需要認識到這一事實。


Web Components 的真正成本

完整的圖景是,處理原生元素的複雜性上升,以適應 Web Components 新發現的靈活性。這不僅是使用它們的用戶在付出代價,所有使用該工具的用戶都在付出代價。需要發送更多代碼,執行更多代碼來檢查這些邊緣情況。這是一種隱性的稅負,影響到每個人。

我已經談到過,早期的標準化將是災難性的。但它也有可能在某些路徑上窒息未來的創新,因為它假設了太多。水合的改進,如可恢復性、部分或選擇性水合,依賴於事件委派來運作。但是,如果 Shadow DOM 擾亂了這一點,那麼 Web Components 又如何適應這一模式呢?有人可能會說,SSR 是一個疏忽,因為我們在 2013 年沒有考慮到太多,但這個差距只是隨著時間的推移而不斷擴大。

如果有什麼可以從編譯器和構建工具的發展中得出的結論,就是我們正越來越多地朝著將組件視為開發者體驗考量的方向發展。你在創建時擁有的東西在最終輸出中消失。為了最佳用戶體驗,我們需要去除組件。

https://dev.to/this-is-learning/components-are-pure-overhead-hpm

我不是在說沒有人能找到有趣的解決方案,但它們都暗示著由於錯誤抽象的基礎不對齊而承擔隱性代價。這就是為什麼這裡的對話如此困難。這不是什麼可以改進的東西。這只是多方面的失誤。

現在,我們都可以說不同的解決方案有不同的取捨。而且也許像 Web Components 這樣的東西只可能在標準機構的支持下成功,因為它需要廣泛存在才能有效運行。這是一個我們正在努力實現的理想,但我們尚未完全達到。

但它真的理想嗎?


重新想像的烏托邦

Image description

在談論微前端或微服務時,我們經常會遇到類似的辯論。從組織上來說,將項目與開發者對齊是有益的。這遵循康威定律

Web Components 允許的隔離或可攜性意味著頁面上可以有來自不同來源的多個不同組件。像其他選擇一樣,這時的謹慎是必要的。與不希望將所有微服務用不同語言編寫相似,你可能不希望在不同的框架中創建所有組件。

但前端的空間要限制得多。每千字節的 JS 成本都很高。不僅僅是維護會讓你不想混合和匹配,還有減少負載的考慮。這就是車輪開始脫離軌道的地方。

如果你的目標是未來保護,那你需要準備在同一頁面上保留不同版本的同一庫。這對 Lit 和 React 都是如此。你可以選擇從不更新任何東西,但這在沒有 Web Components 的情況下也是一種選擇。這裡沒有額外的保證。要未來保護,唯一的選擇便是凍結事物,保持多個版本,並在頁面上承載更多的 JS。

現實情況是,你將以同步的方式更新你的庫,這在任何庫上的情況也一樣。在這些情況下,如果你的頁面上只有一個庫,Web Components 對你來說並沒有任何好處,反而增加了更多的開銷。有可能妨礙當前和未來該庫所提供的特性。你不妨直接使用庫而不使用 Web Components。

第二個考慮因素是粒度。如果你有一個微前端,那它是一個可替換的獨立單元。如果將來你認為這不是處理事務的最佳方式,你可以直接替換。但一旦在各地都採用 Web Components,你將需要解決每一個觸點。Web Components 與微前端和微服務不同,因為它們可以橫向使用。這對標準化來說是好事,但我從未見過一家使用 jQuery 的公司能完全擺脫它。

Web Components 最引人注目的用途是作為一種微前端容器。在這種情況下,你不需要承擔擴展成本,外部溝通最小,並且更容易進行替換。在這種情況下,摩擦足夠小,以至於不需要引入 Web Components。我會因為將 Zendesk 小工具放在我的頁面上而選擇使用它們,但這種抽象是否值得這個成本?


結論

這就是關鍵所在。我無法否認在某些情況下,使用 Web Components 確實是有經驗上的好處。但它在各方面所帶來的代價是沉重的。雖然我不應該急於否定一項技術因為它促使某些不良模式的出現,但當它從不契合理想場景時,這讓我很難支持。在最佳情況下,它只是一種名義上的開銷。

Web Components 是一種徹底的妥協。正如我們所知,有時我們需要妥協。但這並不是什麼值得興奮的事情。而且,有些妥協比其他妥協要好。

有人曾告訴我,十年後,可能沒有任何人會使用 Solid,但 Web Component 仍然會以今天的同一界面存在。但我思考了一會兒,回應說我還是會用 Solid 的,因為這是今日最佳的選擇。即使在 10 年後,我不得不使用一個 10 年前的 Solid 版本,它也會比 Web Component 版本好。十年並不能抹去這一點。希望在十年後,我能使用更好的東西。


原文出處:https://dev.to/ryansolid/web-components-are-not-the-future-48bh


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈