如何在?React?中重拾原生?HTML?屬性
在現(xiàn)代 React 組件開發(fā)中,優(yōu)先想到 useState、useEffect、context、props drilling 這樣的框架能力,而容易忽略:
瀏覽器原生 HTML 屬性本身,就是一個強大而成熟的狀態(tài)表達載體。
比如 data-* 為代表的自定義屬性,在近幾年被越來越多的專業(yè)組件庫采用,如 Radix UI、Headless UI、Ark UI 等。
本文將從基礎到深入,拆解為什么在 React 組件中大量使用原生屬性(尤其是 data-*)是一種更專業(yè)、更可維護、更高性能的工程實踐。
1. data-*:語義擴展與原生兼容性
HTML 原生屬性有一個重要優(yōu)勢:
它們天生是“被設計來給用戶代理(瀏覽器、輔助工具)理解的”。
而 data-* 作為 HTML5 制定的可擴展機制:
- 保證語法合法
- 不破壞 HTML 自身語義
- 與 ARIA 標準兼容
- 支持 CSS、JS 原生讀取
這意味著使用 data-* 做狀態(tài)表達,是天然符合瀏覽器和工具鏈的方式。
2. 提升可訪問性
在構建無障礙(a11y)兼容組件時,一種錯誤做法是:
把組件狀態(tài)(如 open/closed)全部存儲在 React 內部,屏幕閱讀器卻讀不到。
但如果將狀態(tài)同步到 data-state、data-disabled,輔助工具就能更輕松感知 UI 狀態(tài)。例如:
<button data-state="open" aria-expanded="true">Menu</button>
屏幕閱讀器可以根據(jù) ARIA 屬性直接宣布狀態(tài),而 data-state 也能作為冗余狀態(tài)標識用于調試和樣式。
Radix DropdownMenu 的 Trigger
<DropdownMenuPrimitive.Trigger
data-state={open ? "open" : "closed"}
aria-expanded={open}
>
{children}
</DropdownMenuPrimitive.Trigger>Radix 始終同步 data-state 與 aria-expanded——
這樣即便 React 狀態(tài)層出故障,ARIA 與 DevTools 都能明確顯示組件狀態(tài)。
3. 簡化樣式化:CSS 直接響應狀態(tài),避免 JS 再渲染
傳統(tǒng)方式:
- React 改狀態(tài) → 組件重新渲染 → className 改變 → 樣式變化
而 data-* 提供了更直接、無阻塞的方式:
[data-state="open"] {
opacity: 1;
transform: scale(1);
}
[data-state="closed"] {
opacity: 0;
transform: scale(0.95);
}完全不需要額外 JS 邏輯。
Tailwind 示例:
<div data-state="open" class="transition data-[state=open]:opacity-100 data-[state=closed]:opacity-0"> </div>
Radix 的 Tabs Root
Radix 的 Tabs Root 會給觸發(fā)項注入:
<Tab data-state={selected ? 'active' : 'inactive'} />CSS 直接響應:
[data-state="active"] {
color: var(--accent);
}優(yōu)點總結
- 更少的 JS 參與 意味著更快
- 避免 React re-render 意味著更穩(wěn)定
- 樣式只靠 CSS cascade 意味著更干凈
4. 框架無關性
React 的 className、state、useMemo、useCallback 僅存在于虛擬 DOM 中。
而 data-* 寫在真正的 DOM 節(jié)點上:
- 測試工具(Playwright、Cypress)可直接選擇
- 瀏覽器可直接識別
- SSR 與 SEO 可直接讀取
- 遷移框架時不受代碼結構影響(例如遷移到 Vue/Solid/Svelte)
Radix UI 做得最極致的一點:
它所有組件都輸出沒有樣式的 “primitive DOM 節(jié)點”,
而狀態(tài)全部映射為 data-*:
<div data-disabled data-orientation="vertical"></div>
使之成為一套真正的 headless 組件協(xié)議,而不是 React 專屬 DSL。
5. 性能優(yōu)化:減少不必要的 React re-render
如果用 className 或 props 作為狀態(tài)傳遞,當狀態(tài)變化時,React 必須:
- 重新執(zhí)行組件函數(shù)
- diff 虛擬 DOM
- 再決定是否更新 DOM
但若使用 data-*:
React 只需更新一次根節(jié)點的屬性。
子組件無需 re-render。
Radix Accordion
Accordion 內容展開時只更新觸發(fā)器的 data-state:
<AccordionTrigger data-state={open ? 'open' : 'closed'} />內容本身不會重新渲染,不會額外執(zhí)行 useEffect、useLayoutEffect。
這種模式特別適合:
- 大型表格組件
- 虛擬滾動
- 菜單、Popover、Tooltip 等頻繁開合的復雜交互
6. 調試友好
React 狀態(tài)調試有幾個問題:
- useState 值在 DevTools 中需要額外打開 React 面板
- className 合成后難以識別狀態(tài)來源
- 在復雜組件中狀態(tài)鏈路不清晰
但 data-* 讓調試變得“肉眼直觀”:
<button data-state="open" data-disabled="true">...</button>
你不用打開任何插件,就能立刻看到每個節(jié)點的狀態(tài)。
Radix 團隊在 RFC 中提到:
data-state 與 data-disabled 的主要目的之一,就是增強可調試性。
7. 案例剖析:Radix 的 data-* 狀態(tài)模型
下面根據(jù)源碼梳理一張類圖,邏輯示意,展示 Radix 組件的狀態(tài)是如何“外溢”到 DOM 屬性的:

Radix 的數(shù)據(jù)流是一種精心設計的“漏斗”:
- React 層管理邏輯
- 計算狀態(tài)
- 把狀態(tài)下沉到 DOM 原生屬性
- CSS / ARIA / 工具鏈再根據(jù)這些屬性響應
這是一種非常解耦的模型。
8. Radix DropdownMenu
以 @radix-ui/react-dropdown-menu 為例。
Trigger
const Trigger = React.forwardRef((props, ref) => {
const open = useDropdownMenuContext();
return (
<Primitive.button
ref={ref}
data-state={open ? 'open' : 'closed'}
aria-expanded={open}
{...props}
/>
);
});觸發(fā)器只負責把狀態(tài)表達為:
data-statearia-expanded
完全不關心樣式、動畫、布局。
菜單內容(Content)
<Content
data-state={open ? 'open' : 'closed'}
data-side={side}
data-align={align}
>
{children}
</Content>這些 data-* 使得 CSS 可以精確選擇:
[data-state="open"][data-side="bottom"] {
animation: slideDown 200ms;
}從而達到 交互邏輯與展示邏輯徹底分離。
9. 總結
工作為求效率使用框架合情合理,但個人學習不能只看框架,有時候學學 HTML 也不錯,哈哈,甚至可以幫助我們更好地使用框架。最后總結下各項優(yōu)勢:
| 維度 | 優(yōu)勢 |
|---|---|
| 可訪問性 | 與 ARIA 標準兼容,屏幕閱讀器更容易識別狀態(tài) |
| 樣式化 | CSS 可直接響應狀態(tài),不需要 JS 驅動 class 切換 |
| 性能 | 減少不必要 re-render,復雜組件收益巨大 |
| 框架無關性 | 狀態(tài)直接存在 DOM,可跨框架復用 |
| 調試 | DevTools 可見屬性,定位問題更直接 |
| 工程化 | 支持 Tailwind、設計系統(tǒng)、主題系統(tǒng)等工具 |
到此這篇關于如何在 React 中重拾原生 HTML 屬性的文章就介紹到這了,更多相關React HTML 屬性內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解React Native開源時間日期選擇器組件(react-native-datetime)
本篇文章主要介紹了詳解React Native開源時間日期選擇器組件(react-native-datetime),具有一定的參考價值,有興趣的可以了解一下2017-09-09
react-beautiful-dnd拖拽排序功能的實現(xiàn)過程
這篇文章主要介紹了react-beautiful-dnd拖拽排序功能的實現(xiàn)過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
React腳手架config-overrides.js文件的配置方式
這篇文章主要介紹了React腳手架config-overrides.js文件的配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
詳解使用create-react-app添加css modules、sasss和antd
這篇文章主要介紹了詳解使用create-react-app添加css modules、sasss和antd,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07

