在先前的系列文章中,我們已經探討過原生 HTML 結構與渲染效能的基礎規範:包含在 HTML 大小寫與引號規範:別讓細節成為生產環境的隱形炸彈 中整理的 HTML 屬性與 JSX 駝峰屬性(
className、htmlFor、tabIndex)對照表、在 別再只用 div 與 span:HTML 內聯與區塊元素(Inline vs Block)底層渲染邏輯與常見語法地雷 中剖析的<p>內嵌區塊元素自動閉合機制,以及在 DOM 節點深度與結構扁平化優化實作:避免過度巢狀、降低樣式重算與提升 INP 互動效能 中介紹的 Lighthouse DOM 深度閾值與 CSS Subgrid 扁平化佈局。
本篇將不再重複上述靜態 HTML 規則與基礎屬性表,而是聚焦於現代前端工程的 「React JSX 轉譯與元件封裝下的真實 DOM 結構控制」——解析為什麼把 HTML 拆成獨立元件後會破壞父子層級與 SSR 水合(Hydration),並提供 <React.Fragment> 迴圈鍵值綁定、多態元件(Polymorphic as Prop)動態標籤切換,以及 TypeScript ComponentPropsWithoutRef 屬性透傳與型別約束的完整解決思路。
常見問題(Q&A)與程式碼範本快速跳轉
如果目前正在排查元件拆分後導致 Flexbox / CSS Grid 排版失效、Next.js / React SSR 跳出 Hydration failed 表格結構錯誤,或第三方 UI 元件無法套用外部 className 與 ref 等問題,可以直接點擊下方連結跳轉至對應解答與程式碼範本:
- 常見問題快速跳轉:
- Q1:為什麼在
.map()迴圈中渲染多個相鄰節點時,Fragment 短語法無法傳入key屬性,必須改寫為具名Fragment? - Q2:為什麼將表格的
tr抽離成獨立元件並在外層誤包div,或直接寫table漏寫tbody,會在 React SSR 觸發 Hydration Mismatch 錯誤? - Q3:當父元件需要對子元件掛載
ref、綁定原生 DOM 事件或套用className時,為什麼子元件根節點不適合使用Fragment? - Q4:在 Vue 3 單檔案元件(SFC)中使用多根節點(Multi-root Template)時,為什麼外部傳入的
class或id會消失並觸發主控台警告? - Q5:如何讓封裝好的共用元件(如
Button或Card)既能渲染為button/article,又能在特定情境無縫切換為a或li且不產生多餘外層div?
- Q1:為什麼在
- 可直接套用的生產環境程式碼範本:
如果不趕時間,建議順著下方章節往下讀,接下來會逐一講解相關底層運作原理與實作細節。
一、 為什麼元件化拆分會讓 React JSX 轉譯結果偏離預期的 DOM 結構?
1. 常見的元件封裝寫法與三大 DOM 層級錯位瓶頸
在傳統的單一 HTML 檔案中,標籤之間的父子階層一目瞭然:<ul> 的直接子元素是 <li>、<table> 的 <tbody> 底下直接放置 <tr>,而設定了 display: flex 或 display: grid 的容器則直接控制其第一層子節點的排列。
然而在 React 或 Vue 等元件化框架中,開發者為了複用邏輯,會將一個頁面拆分為數十個獨立的子元件(如 <CardGroup />、<TableRow />、<CustomListItem />)。若子元件在封裝時習慣性地在外層包上一層 <div className="wrapper">,或是漏掉原生屬性的轉發,就會在轉譯為真實 DOM 樹時引發三個常見的結構斷裂問題:
// 常見的組件封裝問題寫法:子組件內部多包一層無語意 div,且未轉發外部原生屬性
function MetricCardGroup({ items }) {
// 陷阱 1:為了滿足單一根節點限制,隨手包了一層 <div>,阻斷父層的 Grid 佈局
return (
<div className="group-wrapper">
{items.map((item) => (
<article key={item.id} className="metric-card">{item.title}</article>
))}
</div>
);
}
1. 子元件外層多包一層包裝 div,阻斷父層 Flexbox / CSS Grid 與 > 子代選擇器:當父容器設定了 .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); } 預期讓三張卡片橫向三欄排列時,由於子元件 <MetricCardGroup /> 內部多回傳了一層 <div class="group-wrapper">,瀏覽器實際收到的直接子元素變成了單一個 div.group-wrapper。結果三張 <article class="metric-card"> 全部被擠在第一欄的同一個格子內垂直堆疊,且所有依賴 CSS 直接子代選擇器(>) 的樣式規則也會全部失效。
圖:子元件內部多包一層 div.wrapper 後,改變了真實 DOM 的父子層級,導致三張卡片被塞進同一個 Grid 儲存格
2. 跨元件拆分破壞嚴格的 HTML 父子語法規範,引發瀏覽器強制移位與 SSR 水合失敗:HTML 標準對於特定標籤有著嚴格的直接子元素限制(例如 <ul> / <ol> 的直接子元素只能是 <li>、<table> 的 <tbody> 底下只能是 <tr>,兩者另外只允許 <script>、<template> 這類 script-supporting 元素;<select> 底下也只接受 <option>、<optgroup>、<hr> 等規範列出的元素)。當開發者將表格列抽成 <DataRowWrapper> 元件卻在外層包了 <div>(產出 <tbody><div><tr>...</tr></div></tbody>),根據 WHATWG HTML 解析規範的 in table body 插入模式,瀏覽器 HTML Parser 會執行錯誤修復(Foster Parenting),把非法的 <div> 移到 <table> 前面,<tr> 則留在 <tbody> 內;到了客戶端 React 執行 hydrateRoot 水合比對時,發現實際 DOM 結構與虛擬 DOM 樹不一致,便會回報 Hydration failed because the server rendered HTML didn't match the client 錯誤,並在客戶端重新產生這棵樹。
圖:在 tbody 子元件內誤包 div 會觸發瀏覽器解析器強制移位,導致 React SSR 水合比對失敗與表格結構崩潰
3. 自訂元件寫死根節點標籤且未轉發 ...restProps,導致外部樣式與無障礙屬性斷裂:原生 HTML 標籤天生支援傳入 className、id、aria-*、data-* 與事件處理器;但當團隊封裝了自訂元件 <UserCard className="mt-4" aria-label="個人資料" />,若元件內部沒有接收並展開剩餘屬性(...restProps),外層傳入的 className 與 aria-label 會被元件忽略,不會出現在渲染出的 DOM 上;此外,若元件內部將根節點寫死為 <div>,當列表容器 <ul> 需要將其作為 <li> 渲染時,就只能被迫寫出 <ul><li><UserCard /></li></ul>,平白增加不必要的 DOM 深度。

圖:自訂元件若未展開 ...restProps 或將根標籤寫死為 div,外部傳入的 className 與 aria-* 屬性就不會出現在渲染出的 DOM 上
2. React JSX 四種元件封裝寫法與真實 DOM 產出對照表
為了在元件化開發中精準掌控最終生成的 HTML 結構,我們可以將常見的四種 JSX 封裝模式及其對真實 DOM 樹的影響整理如下:
| JSX 元件封裝模式 | 轉譯後真實 DOM 產出 | 適用場景與結構影響 |
|---|---|---|
1. 包裹外層容器 (<div>...</div>) | 產生實體 <div> 節點包裹子元素 | 僅適用於該層本身需要獨立背景、邊框或 Flex/Grid 盒模型時;誤用會阻斷父層排版 |
2. 短語法片段 (<>...</>) | 不產生任何 DOM 節點(子元素直接掛載於父層下) | 適合靜態回傳多個相鄰兄弟節點(如 <td> 群組),但不支援傳入 key 屬性 |
3. 具名 <Fragment key={id}> | 不產生任何 DOM 節點(保留 React 對齊用之虛擬鍵值) | 適合在 .map() 迴圈中一次產出多個平級元素(如 dt + dd 或主列 + 展開明細列) |
4. 多態元件 (<Box as="li" />) | 依 as 指定標籤直接輸出單一 <li class="box"> | 兼顧樣式封裝與 HTML 語意,避免在語意標籤外再多包一層冗餘 <div> |
下圖直觀對比了「包裹外層 div」、「使用 React.Fragment / Vue 多根節點」與「使用多態元件 as 模式」三種寫法在轉譯為真實瀏覽器 DOM 樹時的結構差異:
圖:三種 JSX 封裝模式的真實 DOM 產出對比——善用 Fragment 與多態 as 屬性可徹底消除夾在中間的冗餘包裝節點
二、 React JSX 轉譯底層機制與多態元件(Polymorphic Component)設計
1. 從 JSX 語法糖到 _jsx(type, props):為什麼 Fragment 能保持 DOM 結構扁平?
在現代 React(React 17 引入的新版 JSX Transform react/jsx-runtime)中,設定為新版轉譯的編譯器(例如 Babel 7.9.0 以上、TypeScript 4.1 以上)會將 JSX 標籤轉譯為 _jsx(type, props, key) 或 _jsxs() 函式呼叫。例如 <p key="k" id="i" /> 會轉譯為 jsx("p", { id: "i" }, "k"),<>...</> 則轉譯為 jsxs(Fragment, { children: [...] }):
- 當
type為小寫字串(如'div'、'ul'、'tr')時,React DOM 會建立對應的實體 DOM 元素。 - 當
type為React.Fragment(Symbolreact.fragment) 時,React 不會為它建立任何 DOM 元素:依 React 官方文件,用Fragment分組對產生的 DOM 沒有任何影響,結果和這些元素沒有被分組時相同,其內部的子節點會直接成為父層 DOM 容器的子節點;具名Fragment上的key則只供 React 辨識列表項目使用。
這意味著無論你在元件樹中拆分了多少層以 <Fragment> 為根的子元件,最終在瀏覽器 Elements 面板中看到的 HTML 結構依然能保持 100% 扁平,完美維持父容器的 CSS Grid / Flexbox 直接父子關係與 <ul> > <li>、<tbody> > <tr>、<dl> > <dt> + <dd> 等嚴格語法結構。
2. 多態元件(Polymorphic as Prop)與 TypeScript 泛型屬性約束
當一個子元件本身需要掛載 CSS 樣式類別(例如 .card 或 .btn)、接收 ref 或綁定點擊事件時,我們無法使用不產生實體節點的 Fragment。此時若為了語意化而在外層包 <li className="item"><Card /></li>,又會造成多餘的 DOM 層級(關於過度巢狀對樣式計算與 INP 效能的衝擊,請參閱 DOM 節點深度與結構扁平化優化實作)。
實務上面對此類需求,主流設計系統採用 「多態元件(Polymorphic Component)」:允許呼叫端動態決定元件最終渲染的 HTML 標籤,例如 Chakra UI 的 as 屬性、Mantine 的 component 屬性(例如 Mantine 的 Button 預設渲染為 button,可透過 component 改成其他元素)。自行實作時可搭配 TypeScript 的 React.ElementType 與 ComponentPropsWithoutRef<T>,讓編譯器依目前的標籤檢查合法屬性(例如 as="a" 時可以傳 href、傳 disabled 會報錯;as="button" 時 type 只接受 button、submit、reset):

圖:多態元件結合 as 屬性與 TypeScript ComponentPropsWithoutRef<T>,在單一 DOM 節點上同時滿足樣式封裝、語意標籤切換與屬性型別安全
三、 可直接套用的 React JSX 結構控制與多態元件程式碼範本
範本一:使用具名 Fragment 與 key 保持表格 tbody 與描述清單 dl 扁平 DOM 結構
在開發「可展開明細的資料表格(Expandable Data Table)」或「規格鍵值描述清單(<dl>)」時,每一筆資料往往需要同時渲染兩個平級的兄弟節點(例如一列主資料 <tr> 搭配一列摺疊明細 <tr>,或一個 <dt> 標題搭配一個 <dd> 數值)。以下範本示範如何使用具名 <Fragment key={item.id}> 在不插入任何非法外層 <div> 的前提下完成元件化渲染:
import { Fragment } from 'react';
interface ServerRecord {
id: string;
hostname: string;
status: 'online' | 'maintenance';
ipAddress: string;
region: string;
}
/**
* 1. 表格列子組件:回傳兩個相鄰的 <tr>(主資料列 + 明細列)
* 使用 Fragment 確保父層 <tbody> 的直接子元素 100% 為合法的 <tr>
*/
function ServerTableRow({ record, isExpanded }: { record: ServerRecord; isExpanded: boolean }) {
return (
<>
<tr className="server-row-primary">
<td>{record.hostname}</td>
<td>{record.status === 'online' ? '運作中' : '維護中'}</td>
</tr>
{isExpanded && (
<tr className="server-row-detail">
<td colSpan={2}>
{/* 2. 描述清單 <dl>:在迴圈中使用具名 <Fragment key={...}> 直接產出平級的 <dt> 與 <dd> */}
<dl className="server-spec-list">
{[
{ label: 'IP 位址', value: record.ipAddress },
{ label: '機房區域', value: record.region },
].map((spec) => (
<Fragment key={spec.label}>
<dt className="spec-term">{spec.label}</dt>
<dd className="spec-desc">{spec.value}</dd>
</Fragment>
))}
</dl>
</td>
</tr>
)}
</>
);
}
/**
* 父層表格組件:顯式包含 <thead> 與 <tbody>,防止 SSR Hydration Mismatch
*/
export function ServerStatusTable({ records, expandedIds }: { records: ServerRecord[]; expandedIds: string[] }) {
return (
<table className="server-table">
<thead>
<tr>
<th scope="col">主機名稱</th>
<th scope="col">目前狀態</th>
</tr>
</thead>
<tbody>
{records.map((record) => (
<ServerTableRow
key={record.id}
record={record}
isExpanded={expandedIds.includes(record.id)}
/>
))}
</tbody>
</table>
);
}
範本二:支援 as 動態標籤切換與 TypeScript ComponentPropsWithoutRef 型別推導的多態元件
以下範本提供一個型別安全的通用多態容器元件 <SurfaceBox>:預設渲染為 <div>,但放在 <ul> 內可指定 as="li"、作為獨立文章卡可指定 as="article"、作為可點擊卡片連結可指定 as="a",且自動將外部傳入的 className、aria-*、data-* 與事件完整合併轉發至最終的單一 HTML 標籤上:
import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
/**
* 定義多態組件專屬 Props:允許傳入泛型標籤 T(繼承自 React.ElementType)
*/
type SurfaceBoxOwnProps<T extends ElementType = 'div'> = {
as?: T;
variant?: 'elevated' | 'outlined' | 'subtle';
className?: string;
children?: ReactNode;
};
/**
* 結合自訂 Props 與目標 HTML 標籤 T 的所有原生屬性(排除名稱衝突欄位)
*/
type SurfaceBoxProps<T extends ElementType = 'div'> = SurfaceBoxOwnProps<T> &
Omit<ComponentPropsWithoutRef<T>, keyof SurfaceBoxOwnProps<T>>;
/**
* 多態容器組件:零多餘外層包裝,直接輸出指定的語意化 HTML 標籤並轉發所有原生屬性
*/
export function SurfaceBox<T extends ElementType = 'div'>({
as,
variant = 'elevated',
className = '',
children,
...restProps
}: SurfaceBoxProps<T>) {
const Component = as || 'div';
const mergedClassName = ['surface-box', `surface-box--${variant}`, className]
.filter(Boolean)
.join(' ');
return (
<Component className={mergedClassName} {...restProps}>
{children}
</Component>
);
}
// ==========================================================
// 呼叫端使用範例:同一組件在不同 DOM 上下文中自動切換為合法 HTML 標籤
// ==========================================================
export function FeatureShowcase() {
return (
<ul className="feature-grid">
{/* 1. 在 <ul> 內部直接渲染為 <li class="surface-box ...">,保持 ul > li 結構 */}
<SurfaceBox as="li" variant="outlined" data-feature-id="dom-clean">
<h3>保持乾淨的父子層級</h3>
<p>不產生多餘的包裝 div,直接作為列表項目與 Grid 子項。</p>
</SurfaceBox>
{/* 2. 渲染為 <li class="surface-box ..."> 並在內部放置具備 href 型別檢查的 <a> */}
<SurfaceBox as="li" variant="elevated">
<SurfaceBox as="a" href="/docs/architecture" variant="subtle">
閱讀完整架構文件
</SurfaceBox>
</SurfaceBox>
</ul>
);
}
四、 開發者常見問題(Q&A)與排查注意事項
Q1:為什麼在 .map() 迴圈中渲染多個相鄰節點時,Fragment 短語法無法傳入 key 屬性,必須改寫為具名 Fragment?
原因說明:在 JSX 語法規範中,<></> 是專門設計給靜態分組使用的極簡語法糖(Shorthand Syntax),編譯器在解析 <></> 時不會讀取任何屬性括號。因此如果你在 .map() 迴圈中寫 <key={item.id}> 會直接觸發語法解析錯誤(例如 esbuild 回報 Expected ">" but found "=");若寫 <></> 而不加 key,React 則會在主控台輸出 Each child in a list should have a unique "key" prop. 錯誤訊息。
解法:凡是在 .map() 迴圈中需要以 Fragment 作為最外層容器回傳多個元素時,請從 'react' 顯式匯入 Fragment(或寫 <React.Fragment key={item.id}>)。需特別注意的是,Fragment 只接受 key 與 ref(ref 自 React 19.3 起為穩定功能),不接受 className 或 style。
Q2:為什麼將表格的 tr 抽離成獨立元件並在外層誤包 div,或直接寫 table 漏寫 tbody,會在 React SSR 觸發 Hydration Mismatch 錯誤?
原因說明:根據 WHATWG HTML 解析規範的 in table 與 in table body 插入模式,瀏覽器的 HTML 解析器在將伺服器端傳來的 HTML 字串建構為 DOM 樹時,會執行兩項強制規則:
- 若看到
<table>底下直接出現<tr>(未寫<tbody>、<thead>或<tfoot>),瀏覽器會自動在<table>與<tr>之間插入一個隱式的<tbody>節點;但客戶端的 React 虛擬 DOM 裡若沒有寫<tbody>,水合比對時就會發現多了一層<tbody>而報錯。 - 若在
<tr>外層誤包了<div>(例如<tbody><div><tr>),瀏覽器會將該<div>搬移到整個<table>的前面(Foster Parenting),<tr>則留在<tbody>內,伺服器端輸出與瀏覽器解析出的 DOM 結構因此不同。
以 React 19.3 為例,這兩種寫法在開發模式水合時都會先輸出巢狀結構錯誤:漏寫 <tbody> 時是 In HTML, <tr> cannot be a child of <table>. Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by the browser.,<tbody> 內包 <div> 時則是 In HTML, <div> cannot be a child of <tbody>.;接著再回報 Hydration failed because the server rendered HTML didn't match the client.,並在客戶端重新渲染整棵樹。
解法:在 React / Next.js / Nuxt 等支援 SSR 的框架中撰寫 <table> 時,務必顯式寫出 <tbody>(有表頭列時另寫 <thead>);且抽離表格列子元件時,元件根節點必須直接為 <tr> 或 <Fragment>,嚴禁包裹 <div>(至於一般 <p> 內誤包 <div> 的解析行為,請參閱 HTML 內聯與區塊元素底層渲染邏輯與常見語法地雷)。
Q3:當父元件需要對子元件掛載 ref、綁定原生 DOM 事件或套用 className 時,為什麼子元件根節點不適合使用 Fragment?
原因說明:由於 Fragment 在轉譯為真實 DOM 時會完全消失,它在瀏覽器中並沒有對應的 HTMLElement 實體,因此 className、style 與 onMouseEnter 這類 JSX 事件屬性都無處掛載。React 19.3 起 Fragment 可以接收 ref,但拿到的是 FragmentInstance 而不是 DOM 元素:它提供 addEventListener、focus、getClientRects、observeUsing、scrollIntoView 等方法,作用對象是 Fragment 內第一層的 DOM 子節點,沒有 getBoundingClientRect(),也不能套用 className。如果某個子元件(例如 <TooltipTrigger>)需要讓父元件以單一元素量測 getBoundingClientRect() 座標或套用定位 className,仍然需要一個實際的根元素。
解法:
- 若子元件預期渲染單一元素,可採用本文範本二的 多態元件
as模式,在元件內部將...restProps轉發至實際渲染的根元素。React 19 起函式元件可以直接把ref當成 prop 接收;範本二的型別使用ComponentPropsWithoutRef<T>,傳入ref會被 TypeScript 拒絕,需要接收ref時改用ComponentPropsWithRef<T>。 - 若希望將事件與
className直接合併到傳入的單一children子元素上(不產生任何外層標籤),可參考 Radix UI 的Slot(asChild模式),它會把自身的 props 與ref合併到唯一的子元素上。
Q4:在 Vue 3 單檔案元件(SFC)中使用多根節點(Multi-root Template)時,為什麼外部傳入的 class 或 id 會消失並觸發主控台警告?
原因說明:Vue 3 雖然原生支援在 <template> 中直接撰寫多個平級根節點(底層同樣以 Fragment VNode 實作),但根據 Vue 3 官方文件 — Fallthrough Attributes ($attrs),當一個元件只有單一根節點時,外部傳入的 class、style 與 id 會自動繼承到該根節點上;一旦元件有多個根節點,Vue 無法猜測應該把 class 掛在哪一個節點上,因此會放棄自動掛載並輸出 Extraneous non-props attributes (class) were passed to component but could not be automatically inherited 警告。
解法:在 Vue 3 多根節點元件中,請在真正需要接收外部樣式與屬性的目標元素上顯式綁定 v-bind="$attrs"(例如 <li v-bind="$attrs">),即可消除警告並精準控制屬性落點。
Q5:如何讓封裝好的共用元件(如 Button 或 Card)既能渲染為 button / article,又能在特定情境無縫切換為 a 或 li 且不產生多餘外層 div?
原因說明:在實務介面中,外觀相同的按鈕有時是觸發儲存的 <button type="button">,有時則是跳轉頁面的 <a href="/login">;若將 <Button> 寫死為 <button> 並在外面套 <a href="/login"><Button>登入</Button></a>,會產生 <a> 內部嵌套 <button> 的非法互動元素衝突(WHATWG 規範 <a> 元素不得包含互動內容後代;兩者又都是可聚焦元素,鍵盤 Tab 會在同一個按鈕上停留兩次)。
解法:採用本文第 2.2 節與範本二的 多態元件(Polymorphic as Prop) 設計,讓 <Button as="a" href="/login">登入</Button> 直接在底層將根標籤替換為單一個 <a class="btn" href="/login">登入</a>,同時維持視覺樣式一致與 HTML 語意合法。
五、 總結與官方參考文獻
掌握 React JSX 轉譯與真實 DOM 結構的對應關係,核心在於養成「透過元件邊界預見最終 HTML 階層」的工程習慣:
- 區分「元件邏輯邊界」與「DOM 盒模型邊界」:拆分 React / Vue 子元件時,若該元件不需要獨立的視覺盒模型,優先使用
<>...</>或<Fragment key={id}>,避免無意間塞入外層<div>破壞父容器的 Flexbox、CSS Grid 或>子代選擇器。 - 嚴格遵守特定 HTML 容器的直接子元素規範:在封裝
<table>(<thead>/<tbody>/<tr>)、<ul>/<ol>(<li>)、<dl>(<dt>/<dd>)與<select>(<option>)的子元件時,務必確保轉譯後的直接父子關係合法,防止觸發瀏覽器強制移位與 SSR Hydration Mismatch。 - 善用多態元件(
asProp)與...restProps屬性透傳:針對需要掛載樣式類別或ref的共用 UI 元件,透過泛型as屬性與ComponentPropsWithoutRef<T>讓呼叫端靈活指定最終輸出的語意化標籤(需要接收ref時改用ComponentPropsWithRef<T>)。
站內系列文章與官方權威規範延伸閱讀
- 站內相關 HTML 與 DOM 結構系列筆記:
- DOM 樹狀結構與渲染引擎揭秘:Reflow (重排) 與 Repaint (重繪) 的效能優化全要點(解析瀏覽器如何將 HTML Token 組裝為記憶體中的 C++ DOM 節點樹)
- DOM 節點深度與結構扁平化優化實作:避免過度巢狀、降低樣式重算與提升 INP 互動效能(解析過深 DOM 巢狀對 CSS 選擇器回溯與 INP 互動延遲的影響)
- 別再只用 div 與 span:HTML 內聯與區塊元素(Inline vs Block)底層渲染邏輯與常見語法地雷(解析
<p>包<div>自動閉合與<a>透明內容模型規則) - HTML 大小寫與引號規範:別讓細節成為生產環境的隱形炸彈(含原生 HTML 屬性與 React JSX 屬性名稱對照矩陣)
- React、Vue 與 WHATWG 官方技術規格:
- React Official Documentation —
<Fragment>(<>...</>) Reference - React Official Documentation —
hydrateRoot& Fixing Hydration Mismatch Errors - React Blog — React 19.3(Fragment Refs 轉為穩定功能)
- React Blog — Introducing the New JSX Transform
- Vue.js Official Guide — Fallthrough Attributes & Attribute Inheritance on Multiple Root Nodes
- React Blog — React v19(
refas a prop) - WHATWG HTML Living Standard — The
tbodyelement - WHATWG HTML Living Standard — The
ulelement - WHATWG HTML Living Standard — The
selectelement - DefinitelyTyped —
@types/react(ComponentPropsWithoutRef型別定義) - WHATWG HTML Living Standard — Parsing: in table body insertion mode
- Mantine — Polymorphic components
- Radix Primitives — Slot
- MDN Web Docs — CSS Child combinator (
>)
- React Official Documentation —



發佈留言