標記優先:先把結構講清楚,行為再說

我寫一個畫面的時候,習慣先問結構長怎樣,而不是先問要用哪個元件。這順序聽起來小事,但它決定了後面好不好維護、好不好除錯。多數框架教學是反過來教的,先給你一個元件,再叫你把資料塞進去,結構反而被元件的生命週期跟狀態管理糊掉,你最後對「瀏覽器實際渲染出什麼」沒了直覺。

先有結構,再包行為

HTML、Markdown、XML 這類標記語言,本質是在描述「這份文件由哪些東西組成、它們的層級關係是什麼」。這是一種宣告式的結構表達,跟「要怎麼做」無關。我偏好讓這層先穩下來:標題就是標題,清單就是清單,表單就是表單,語意對了,無障礙、SEO、可讀性大半就直接拿到。

行為這層應該是輕量包上去的。一個按鈕點下去發一個請求、把回來的片段塞進某個容器,這種互動其實不需要先有一套元件框架。htmx 把這件事做得很俐落,互動寫成 HTML attribute,伺服器主導要回什麼,client 端幾乎不留狀態。我不是說所有東西都該這樣寫,高互動、離線、canvas、local-first 那類應用本來就需要 client 端狀態管理,這沒得避。但對大部分以內容跟表單為主的頁面,預設值該是「結構優先、行為薄一層」,而不是反射性地先架一個 SPA。

當你從 server 回傳的是「一段已經長好的 HTML」,而不是「一包 JSON 再讓 client 重新組 DOM」,心智負擔差很多。前者你打開 devtools 看到什麼就是什麼,後者你得在腦中模擬一遍 render 流程才知道畫面為何長這樣。

attribute 不是 property

有個區別被框架糊掉很久,但其實很關鍵:HTML attribute 跟 DOM property 不是同一件事。

attribute 是你寫在標記裡的初始值,它描述「這個元素一開始的標記狀態」。瀏覽器解析 HTML 之後,會建出 DOM 節點,節點上的 property 才是執行期那個活的、會變的狀態。兩者常常同名,值卻會分岔。經典例子是 input:你在 HTML 寫 value="hello",使用者打了字之後,element.value 變成新的內容,但 element.getAttribute("value") 還是 hello。一個是初始標記,一個是當下狀態。checkeddefaultCheckedclassclassList 也是類似的分界。

<input value="hello">
<!-- 使用者改成 world 之後 -->
<!-- element.value            => "world"  (DOM property,活的) -->
<!-- element.getAttribute()   => "hello"  (HTML attribute,初始) -->

框架的抽象只是把這個分界「減少你直接碰它的機會」,沒有消除它。你在 React 寫 value={state}、在別的框架做雙向綁定,底層還是有人在幫你把 property 跟某個狀態源同步。等到哪天綁定行為怪怪的、表單值對不上、第三方套件直接操作 DOM 跟框架打架,你就得回到這個分界去想:現在是 attribute 沒更新,還是 property 被誰偷改了。懂這條線的人,除錯速度差很多,因為他知道該去 inspect 哪一邊。

從畫 DOM 樹開始

實務上我會建議,設計一個畫面先在紙上或腦中把 DOM 樹畫出來:根節點是什麼、底下分幾塊、每塊的語意標籤是什麼、哪裡需要可互動。這棵樹畫清楚了,要套什麼框架、要不要拆元件,都是後面的事,而且會變得很自然,因為元件邊界往往就落在樹的自然分界上。

這樣做維護體驗好很多。出問題的時候,你的 mental model 跟 devtools 裡的 Elements 面板是對齊的,你看著真實 DOM 就能往回推;而不是對著一堆元件名稱跟 props,猜它們最後展開成什麼。除錯本質上是在「我以為的結構」跟「實際的結構」之間找落差,這兩者越接近,找落差越快。

還有一個長期觀察。軟體疊得越厚,工具鏈越多層,使用者跟工程師反而會回頭找更薄、更透明的表達方式。純文字、Markdown、各種標記語言之所以耐久,是因為它們可以直接讀、可以 diff、可以丟進版本控制看每一行怎麼變的。一個用十層抽象生出來的畫面,壞掉的時候你很難 diff 出「到底哪一層改了什麼」;一份 HTML 或 Markdown,壞了你一眼就看到。

說到底這不是懷舊,不是覺得以前的東西比較好。是因為結構優先的東西可控、可解釋:可控,因為層級簡單、每層責任清楚;可解釋,因為它就擺在那裡,不需要你跑一遍框架才知道結果。求的就是這兩個。

重點整理

  • 設計畫面先描述結構,再包行為,不要反過來從框架元件開始想。
  • HTML attribute 是初始標記狀態,DOM property 是執行期活的狀態,框架只減少、沒消除這條分界。
  • 懂 attribute / property 分界的人除錯快,因為知道該 inspect 哪一邊。
  • 從畫 DOM 樹開始,mental model 跟 devtools 對齊,找落差就快。
  • 軟體越疊越厚,純文字與標記語言因為可讀、可 diff、可版控而更耐久。

觀點 Sheng,內文 Claude 協助 · 列入 20260613 blog 翻新計劃,新漆未乾。