引言
瀏覽器這玩意兒現在真夠詭異的。WebAssembly 在服務器端混得風生水起,但客户端還是那副老樣子,跟十年前沒啥區別。
WASM 粉會跟你吹,通過點 JS 膠水代碼就能調原生 Web API。但核心問題是:為啥非得用 DOM?這東西就是個默認選項罷了。本文直擊 DOM 和相關 API 的痛點,為什麼該讓它們退場了,順便腦洞下怎麼改進。
作者不是瀏覽器全棧專家——沒人能全懂了,這正是癥結所在:東西太雜太亂。
DOM 的“文檔”模型:臃腫得像個大胖子
DOM 爛到什麼程度?Chrome 裏document.body有 350+個鍵值,大致分類:
- 節點操作:appendChild、removeChild之類的。
- 樣式相關:style對象塞了 660 個 CSS 屬性。
- 事件處理:那些過氣的onevent屬性,比如onclick,基本沒人用了。
- 雜七雜八:innerHTML、className等。
屬性和方法界限模糊,很多 getter 會偷偷觸發重排,setter 藏在暗處。還有一堆歷史遺毒。
DOM 不瘦身,還在發福。你是否感受到這痛苦,取決於你是搞靜態頁還是 Web App。作為開發者,我們大多避開直接操 DOM,轉而用框架。但偶爾有純 DOM 黨吹它牛逼——純屬自欺欺人。DOM 的聲明式功能,比如innerHTML,跟現代 UI 模式八竿子打不着。同一件事 DOM 有 N 種方式,全都不優雅。
Web Components 的尷尬處境
Web Components 是瀏覽器原生組件方案,但來得太晚,人氣不高。API 設計笨重,Shadow DOM 加了層嵌套和作用域,調試起來頭大。粉絲的辯護聽着像在找藉口。以下是一個簡單的示例:
class HelloWorld extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'closed' });
const template = document.getElementById('hello-world').content.cloneNode(true);
const hwMsg = `Hello ${this.getAttribute('name')}`;
Array.from(template.querySelectorAll('.hw-text')).forEach(n => n.textContent = hwMsg);
shadow.append(template);
}
}
customElements.define('hello-world', HelloWorld);
看起來還行?但實際開發中,Shadow DOM 的複雜性和 DOM 的字符串化特性(stringly typed)讓開發者頭疼。相比之下,React、Vue 等框架的虛擬 DOM 完全避開了這些問題,因為它們的語法只是“長得像 XML”,而不是真的依賴 DOM。
HTML 的停滯不前
HTML10-15 年沒大動靜。ARIA 是亮點,但只是補語義 HTML 的漏。語義 HTML 從 2011 年就開始推,但到現在都沒\<thread\>或\<comment\>標籤。嵌套\<article\>來模擬評論?指導原則也奇葩。
HTML 總像在嫉妒紙媒,沒能真正擁抱超文本本質,也不信開發者能守規矩。
WHATWG(瀏覽器廠商)接管後,沒啥願景,就在邊邊角角加補丁。CSS 甚至長出了表達式——每個模板語言都想變編程語言。
編輯 HTML?contentEditable理論上行,但實際搞成可用編輯器是黑魔法。Google Docs 和 Notion 的工程師肯定有吐不完的槽。
漸進增強、 markup/style 分離?做 App 的開發者早不信這套了。
現在 App 大多用 HTML/CSS/SVG 拼湊 UI,但開銷巨大,越來越不像正經 UI 工具箱。
比如 Slack 的輸入框:用一堆 div 模擬富文本。剪貼板 hack 用隱藏元素。列表/表格得手動虛擬化,自管佈局、重繪、拖拽。聊天窗滾動條粘底每次都得重寫。虛擬化越深,越得重造頁面搜索、右鍵菜單等。
Web 混淆了 UI 和流式內容,當年新鮮,現在過時。UI 陳舊,內容同質化。
CSS 的“內外倒掛”:別用錯心智模型
CSS 口碑一般,但問題在哪?很多人誤以為它是約束求解器。看這例子:
<div>
<div style="height: 50%">...</div>
<div style="height: 50%">...</div>
</div>
<div>
<div style="height: 100%">...</div>
<div style="height: 100%">...</div>
</div>
第一個想分一半高?第二個自相矛盾?實際 CSS 忽略height,父元素收縮包裹內容。
CSS 是兩趟約束:先外到內傳尺寸,再內到外集內容大小。App 佈局外到內:分空間,內容不影響面板大小。文檔內到外:段落撐開父級。
CSS 默認內到外,文檔導向。要外到內,得手動傳約束,從body { height: 100%; }開始。這就是垂直對齊難的原因。
Flexbox 給顯式控制:
用flex-grow/shrink做無溢出自適應佈局,加 gap 間距。
但 Flex 混淆了簡單模型:需先“猜測”子自然尺寸,佈局兩次——一次假設浮空,一次調整。遞歸深了可能爆棧,雖少見,但大內容一丟,一切變形。
避坑:用contain: size隔離,或手動設flex-basis。
CSS 有contain、will-change這類直擊佈局的,暴露底層層級本質。代替position: absolute包裹。
本質上,這些切斷 DOM 全局約束流——默認太寬泛,太文檔化。
CSS 的好地方?
Flexbox 懂了這些坑,還挺靠譜。嵌套行列+gap,直觀適配尺寸。CSS 好部分在這,但得用心打磨。Grid 類似,但語法太 CSS 味兒,囉嗦。
從零設計佈局,不會這樣:不會用減法 API 加屏障提示。會拆成組件,用外/內容器+放置模型,按需組合。
inline-block/inline-flex示意:內部 block/flex,外部 inline。盒模型兩正交面。
文本/字體樣式是特例:繼承如font-size,為\<b\>工作。但 660 屬性大多不繼承——邊框不遞歸子級,那會傻。
CSS 至少兩東西混搭:繼承的富文本樣式系統 + 非繼承的嵌套佈局系統。用同語法/API 是錯。
em相對縮放過時,現在邏輯/設備像素更 sane。
SVG 無縫入 DOM,動態形狀/圖標調色。但 SVG 非 CSS 子/超集,重疊處微差,如transform。座標字符串化,煩。
CSS 加圓角/漸變/剪裁,有 SVG 嫉妒,但遠不及。SVG 做多邊 hit-testing,CSS 不行。SVG 有自己圖形效果。
選 HTML/CSS 還是 SVG?基於具體 trade-off,全是向量後端。
注意一下的坑:
- text-ellipsis只截單行文本,非段落。檢測/測量文本 API 爛,大家數字母湊合。
- position: sticky零抖動滾動固定,但有 bug。無條件 sticky 需荒謬嵌套,本該簡單。
- z-index絕對層級戰,z-index-war.css 裏 +1/-1 比拼。無相對 Z 概念。
API 設計難,得迭代建真東西,找漏。
SVG 與 CSS 的權衡
SVG 在 Web 中用於動態生成圖形或調整圖標樣式,但它與 CSS 並非完全兼容。例如,SVG 的 transform 與 CSS 的變換屬性有微妙差異,且 SVG 的座標全是字符串序列化,增加了開發複雜性。
國內場景:假設你在開發一個數據可視化儀表盤,類似 ECharts 的柱狀圖。你可以選擇用 SVG 繪製圖形,或者用 CSS 實現類似效果。SVG 支持多邊形點擊檢測(hit-testing),而 CSS 不行;但 CSS 的圓角、漸變等功能又讓 SVG 顯得多餘。最終,你可能需要在兩者間做痛苦的權衡。
Canvas 上的油畫:HTML in Canvas 的坑
DOM 壞,CSS 幾成好,SVG 醜但必備……沒人修?
診斷:中間層不合用。HTML6 先砍東西起步。
但關鍵解放現有功能。理想:用户空間 API 同逃生口,狗食自家。
HTML in Canvas 提案:畫 HTML 到\<canvas\>,控視覺。不是好法。
API 形因塞 DOM:元素須 canvas 後代參與佈局/樣式/無障礙。離屏用有“技術關切”。
例子:旋轉立方體交互用 hit 矩形+paint 事件。新 hit API,但只 2D——3D 純裝飾?問題多。
從零設計,不會這樣!尤其瀏覽器有 CSS 3D transform,何須為自定義渲染全接交互?
未覆蓋用例如曲面投影,需複雜 hit。想過下拉菜單嗎?
像沒法統 CSS/SVG 濾鏡,或加 CSS shader。經 canvas 是剩選項。“至少可編程”?截 DOM 一好用,但非賣點。
Canvas 上覆雜 UI 是為繞 DOM:虛擬化、JIT 佈局/樣式、效果、手勢/hit 等。全低級。預備 DOM 內容反生產。
反應性上,路由回同樹設循環。渲染 DOM 的 canvas 非文檔元素了。
Canvas 真痛:無系統字體/文本佈局/UI 工具。從零實現 Unicode 分詞,就為包裹文本。
提案“DOM 黑箱內容”,但知套路:還得 CSS/SVG 拼湊。text-ellipsis仍破,得從 90 年代 UI 重造。
全或無,要中道。下層需開。
未來的方向:重新設計 DOM
DOM 和 CSS 的問題根源在於它們揹負了太多的歷史包袱。以下是一些可能的改進方向:
- 精簡的數據模型:未來的 DOM 需要大幅減少屬性數量(從 350+精簡到幾十個),專注於核心功能。類似 React 的虛擬 DOM,但直接內置於瀏覽器中。在開發類似頭條的信息流應用時,開發者需要快速渲染大量卡片。精簡的 DOM 模型可以減少不必要的 API 調用,提高性能。
- 統一的佈局系統:將 CSS 的內外佈局模式明確分開,支持更直觀的“外部約束”和“內部自適應”。例如,垂直居中應該像 align-items: center 一樣簡單。在電商平台的商品詳情頁中,開發者希望輕鬆實現複雜的佈局(例如商品圖片和描述的動態對齊),而不是依賴一堆 CSS hack。
- WebGPU 的潛力:WebGPU 提供了更底層的渲染能力,可以完全拋棄 DOM 的複雜性。例如,Use.GPU 項目展示了一個基於 WebGPU 的簡潔佈局系統,代碼量僅為傳統 DOM/CSS 的幾分之一。在開發類似 B 站的彈幕播放器時,WebGPU 可以用來高效渲染動態彈幕,省去 DOM 的性能開銷。
- 多線程與隔離:現代瀏覽器已經是多進程架構,但 DOM 的設計沒有跟上。未來的 DOM 需要支持更好的多線程和跨源隔離,適應複雜的 Web 應用需求。在企業級應用(如釘釘的協作平台)中,開發者需要集成第三方服務(如 OAuth 登錄)。一個支持多線程的 DOM 可以顯著提高安全性和性能。
結論
HTML、CSS 和 DOM 的現狀就像一輛老舊的馬車,雖然還能跑,但早已不適合現代 Web 應用的複雜需求。國內開發者在開發小程序、電商平台或社交應用時,常常需要用框架和 hack 來彌補 DOM 的不足。未來的 Web 需要一個更精簡、更靈活的渲染模型,可能基於 WebGPU 或全新的 API 設計。
與其修補 DOM 的漏洞,不如從第一性原理出發,重新設計一個適合現代應用的 Web 渲染層。就像當年的 Netscape 開啓了 Web 時代,今天的我們也有機會重新定義瀏覽器的未來。