JSX 與 HTML 的差異:5 個常見新手錯誤一次搞懂
更新日期: 2025 年 3 月 30 日
本文為 JSX 基礎介紹系列文:
- React 中的 JSX 是什麼?為什麼它長得像 HTML?
- JSX 與 HTML 的差異:5 個常見新手錯誤一次搞懂
- JSX 中如何插入 JavaScript?使用 {} 的基本原則
- JSX 中的條件渲染與清單渲染:實用範例教學
- JSX 背後的真相:它是怎麼轉換成畫面的?
閱讀完本系列文後,可以繼續閱讀《React 元件基礎介紹》介紹
當你開始學習 React 時,第一個遇到的語法通常就是 JSX。
它看起來很像 HTML,但實際上是 JavaScript 的一部分。這種「像 HTML 的 JavaScript」語法雖然直覺、易讀,卻也常常讓初學者混淆。
本篇文章將帶你認識 JSX 與 HTML 的差異,並透過「5 個常見新手錯誤」來幫助你避開陷阱、順利寫出正確的 React 元件!
class 不能用了?用 className
才對!
當你從 HTML 轉換到 JSX 時,第一個會撞到的牆,通常就是這個:
為什麼明明在 HTML 裡寫了無數次的 class="..."
,到了 JSX 卻會出錯?
⛔ 錯誤用法(HTML 習慣)
<div class="container"></div>
✅ 正確寫法(JSX 規則)
<div className="container"></div>
為什麼不能用 class
?
這是因為在 JavaScript 語言本身中,class
是保留字(reserved keyword),用來定義類別,例如:
class Person {
constructor(name) {
this.name = name
}
}
JSX 雖然看起來像 HTML,但實際上是 JavaScript 的語法擴充(語法糖),最終會被編譯成 JavaScript 函式呼叫。
所以當你在 JSX 裡寫 <div class="container">
,JavaScript 會搞不清楚你是想定義一個類別還是給元素加上樣式 class,導致語法錯誤。
為了避免這個衝突,React 特別設計了替代屬性名稱 className
,來代表 HTML 中的 class
屬性。
✅ 實際範例:如何正確為 JSX 元素加上 class
function App() {
return (
<div className="box active">
<h1 className="title">歡迎來到 React 世界!</h1>
</div>
)
}
在這個範例中,我們為 div
和 h1
加上了多個 class,完全依照我們過去寫 HTML 的習慣,只是將 class
改成了 className
。
常見誤解:那 className 是 HTML 的標準嗎?
不是。
className
並不是 HTML 的標準屬性,它是 React 專門為 JSX 設計的對應屬性。
它在被 React 處理後,最終仍會變成標準 HTML 的 class
:
<div className="container"></div>
會被轉換成:
<div class="container"></div>
所以瀏覽器看到的還是熟悉的 HTML 格式,不用擔心。
📌 小提醒與實戰建議
- 千萬不要混用:你可能會想「我改一半先試試看」,但
class
在 JSX 裡是錯誤語法,會直接報錯,甚至導致整個組件無法渲染。 - VSC 或 IDE 提示:現代開發工具大多會直接提醒你
class
是錯誤屬性,並建議使用className
,學會接受這個錯誤訊息是新手的重要一步。 - 記憶口訣:「JSX 用 JavaScript 語法,class 要改 className」。
style 要改寫成物件,而且要駝峰命名
在寫 HTML 時,我們習慣使用類似 CSS 的語法,把 style 寫成一整段字串。
但到了 JSX 裡,這樣的寫法會造成錯誤,因為 JSX 背後是 JavaScript,寫法也就必須符合 JavaScript 的邏輯。
⛔ 錯誤用法(HTML 習慣)
<div style="background-color: red; font-size: 16px;"></div>
這種寫法在 HTML 裡沒問題,但放到 JSX 裡會報錯!
✅ 正確寫法(JSX 規則)
<div style={{ backgroundColor: 'red', fontSize: '16px' }}></div>
看起來是不是有點奇怪?雙層大括號?屬性也變得怪怪的?
別擔心,下面一步一步解釋給你聽。
為什麼直接寫字串不行?因為 JavaScript 的 style
本來就不是字串!
當你在 JSX 裡寫這樣的程式碼:
<div style="color: red;"></div>
React 會將它轉譯為:
React.createElement('div', { style: 'color: red;' })
你會以為你只是幫這個 <div>
加上紅色字體,但實際上,你是在 JavaScript 中把一段字串傳給 style
屬性。
問題來了:在 JavaScript 的世界裡,style
這個屬性本身不是用來接收字串的,而是 DOM 中一個特殊的物件,叫作 CSSStyleDeclaration。
也就是說,你不能對它傳一段字串,它不會自己解析成 CSS,而是要一個個屬性設定。
📌 JavaScript 中操作樣式的正確方式:物件屬性設定
我們來看一個更具體的例子:
const div = document.createElement('div') // 建立一個 <div> 元素
div.style.color = 'red' // 設定文字顏色
div.style.fontSize = '16px' // 設定文字大小
這段程式碼完全不需要字串拼接,它是直接使用 style
屬性上面的每個樣式鍵,像是 .color
、.fontSize
,逐一賦值來完成樣式設定。
換句話說,style
其實是一個JavaScript 物件,你對它操作的方式,是用「屬性」的方式而不是「一段字串」。
🔄 對應到 React:JSX 是 JavaScript,當然也要用物件
React 就是遵守這個原則,當你在 JSX 裡寫樣式的時候,style
屬性也被設計成接受 JavaScript 物件。
所以正確的 JSX 寫法是:
<div style={{ color: 'red', fontSize: '16px' }}></div>
React 背後會根據這個物件的每個 key-value,呼叫 DOM 的 element.style.xxx = ...
一一套用,結果和你用 div.style.color = 'red'
是一樣的效果。
🤓 小知識補充:style="..."
在瀏覽器裡是例外機制
你可能會想問:「可是在 HTML 裡寫 style="..."
不是很好用嗎?」
這是因為 HTML 文件格式被瀏覽器特別設計來「容許你寫一整段 CSS 字串」,所以它可以解析:
<div style="background-color: blue; font-size: 18px;"></div>
但你一旦進入 JavaScript,特別是在 React 的 JSX 中,這種方便的機制就不再適用,而是回歸 JavaScript 本身的標準語法,也就是「使用物件、逐一賦值」的方式來設定樣式。
正確寫法:傳入 JavaScript 樣式物件
React 要求你將 style
寫成 JavaScript 的物件格式,像這樣:
<div style={{ color: 'red', fontSize: '16px' }}></div>
是不是看到兩層大括號覺得有點奇怪?
來拆解一下它的結構:
- 外層
{ ... }
:代表這是一段 JavaScript 表達式(JSX 語法) - 內層
{ ... }
:是實際的樣式物件(JavaScript 的 Object)
這樣 React 就可以把你傳進去的物件,正確地對應到 DOM 元素的樣式屬性,最終產生出對應的 CSS:
<div style="color: red; font-size: 16px;"></div>
✅ 實戰範例:把 HTML 寫法改成正確的 JSX 寫法
功能 | HTML 寫法 | JSX 寫法 |
---|---|---|
背景顏色 + 字體大小 | style="background-color: red; font-size: 16px;" | style={{ backgroundColor: 'red', fontSize: '16px' }} |
粗體文字 | style="font-weight: bold;" | style={{ fontWeight: 'bold' }} |
邊框樣式 | style="border: 1px solid black;" | style={{ border: '1px solid black' }} |
💡延伸:style 也可以用變數管理
為了讓樣式更清楚、可重用,你也可以把樣式物件存在變數裡:
const boxStyle = {
backgroundColor: 'lightblue',
padding: '10px',
borderRadius: '8px'
}
function App() {
return <div style={boxStyle}>Hello, styled box!</div>
}
這樣寫會讓組件更清晰、方便維護。
JSX 中只能回傳一個根元素(Single Root Element)
在寫 React 元件時,很多初學者常會遇到這樣的錯誤狀況:
⛔ 錯誤寫法:想回傳多個標籤
return (
<h1>Hello</h1>
<p>World</p>
)
這樣寫在 HTML 中完全合理,畢竟瀏覽器可以解析一串 <h1>
和 <p>
並正常渲染。但在 JSX 中,這樣會直接導致語法錯誤!
🤔 錯在哪裡?JSX 不允許「平行元素」
React 中的 JSX 語法有一個基本規則:
每個元件的
return
語法中,只能回傳 一個單一的最外層元素(root element)。
換句話說,上面的寫法 React 會這樣理解:
return [
React.createElement('h1', null, 'Hello'),
React.createElement('p', null, 'World')
]
但這會讓 React 搞不清楚「這兩個元素誰是根元素」,就像一棟房子如果有兩個地基,React 不知道要從哪一個開始蓋,結果就是報錯。
✅ 正確做法:用容器把多個元素包起來
你只要用一個包覆元素(wrapper element)把所有東西包起來,就可以滿足「只有一個根元素」的規則。最常見的做法是用 <div>
:
return (
<div>
<h1>Hello</h1>
<p>World</p>
</div>
)
這樣就成功了!因為現在整個 return
回傳的東西,只有一個根 <div>
,裡面再包著兩個子元素。
替代方案:使用 React Fragment(不產生多餘的 DOM)
有時候你只想要包起來,但又不想真的多一層 <div>
出現在畫面中(避免產生不必要的 DOM 結構),這時可以使用 React Fragment:
return (
<>
<h1>Hello</h1>
<p>World</p>
</>
)
這裡的 <></>
是 Fragment 的縮寫語法,它不會被渲染成任何 HTML 元素,但能夠達到「包住多個元素」的效果。
你也可以寫成完整語法:
return (
<React.Fragment>
<h1>Hello</h1>
<p>World</p>
</React.Fragment>
)
兩者功能完全一樣,差別只在語法形式。縮寫語法更簡潔、常用於日常開發。
📌 小提醒與實務建議
- 每一個 JSX
return
的最外層只能有一個元素,這是語法規定,不是選擇。 - 若只是為了包住多個元素但又不想污染 DOM 結構,建議使用
<>...</>
(Fragment)。 - Fragment 在元件層級管理時很有用,例如在列表、條件渲染中,能保持結構簡潔。
實際對比:多個元素回傳方式比較
寫法 | 是否正確 | 是否會產生多餘 DOM |
---|---|---|
多個元素平行 return | ❌ 錯誤 | – |
使用 <div> 包住 | ✅ 正確 | ✅ 會產生 <div> |
使用 Fragment 包住 | ✅ 正確 | ❌ 不會產生 DOM 元素 |
屬性命名要用駝峰式(camelCase)
⛔ 錯誤寫法:照抄 HTML 的寫法會出錯
許多初學者在寫 JSX 的時候,會直覺地把 HTML 裡熟悉的事件寫法搬過來,例如:
<input type="text" onclick="handleClick()" />
這在 HTML 裡的意思是:當使用者點擊這個輸入框時,執行 handleClick()
這個函式。
沒問題,瀏覽器可以解析這段字串並觸發對應的 JavaScript 函式。
但在 React 中,這樣寫會直接報錯或根本沒有效果,原因是 JSX 背後的運作邏輯和 HTML 完全不同。
🤔 為什麼這樣寫會錯?來看 React 如何轉譯這段程式碼
React 並不是直接將 JSX 原封不動地呈現在畫面上,而是先把 JSX 編譯(轉譯)成 JavaScript 的函式呼叫,像這樣:
⛔ 錯誤寫法:
<input type="text" onclick="handleClick()" />
React 背後的轉譯結果:
React.createElement('input', {
type: 'text',
onclick: 'handleClick()'
})
分析一下這裡出了什麼問題:
- 事件屬性名稱是錯的
- HTML 裡事件是小寫的
onclick
,但在 JSX 中要遵守 JavaScript 的命名規則 → 駝峰式onClick
。 - 寫成
onclick
,React 會把它當成「一般的屬性」,加在 DOM 上,根本不會綁定事件。
- HTML 裡事件是小寫的
- 事件處理方式是錯的
- 你傳進去的是一個字串
"handleClick()"
,React 不會主動執行這段字串(不像瀏覽器會解析 HTML)。 - React 要求你傳入的是一個「JavaScript 函數參照」,而不是字串形式的程式碼。
- 你傳進去的是一個字串
✅ 正確寫法應該長這樣:
<input type="text" onClick={handleClick} />
正確寫法的 React 轉譯結果:
React.createElement('input', {
type: 'text',
onClick: handleClick
})
這就完全正確了!
onClick
是 JavaScript 中合法的屬性命名方式(駝峰式),React 能夠辨識這是一個事件處理器。handleClick
是一個函數參照,等到使用者真的點擊時,React 才會幫你呼叫它。
還有哪些屬性也要用駝峰命名?
除了事件屬性之外,還有許多 HTML 常用屬性,在 JSX 裡都需要改寫成 JavaScript 風格:
HTML 屬性 | JSX 寫法 | 說明 |
---|---|---|
onclick | onClick | 所有事件都要用駝峰式命名 |
tabindex | tabIndex | 控制鍵盤瀏覽順序 |
readonly | readOnly | 設定為唯讀輸入 |
maxlength | maxLength | 限制輸入字元數量 |
for | htmlFor | <label> 元素專用(因為 for 是 JS 保留字) |
📎 小提醒:函式不要加引號也不要加小括號!
這是另一個常見錯誤源頭:
錯誤寫法 | 問題原因 |
---|---|
"handleClick()" | 這是字串,React 不會執行它 |
{handleClick()} | 這是「立刻執行」,不是傳參考 |
{handleClick} | ✅ 正確!傳入函式參考,事件發生時才執行 |
JSX 中的內容是 JavaScript 表達式!
在 React 中,JSX 是用來描述 UI 結構的語法糖,看起來像 HTML,但其實它和 JavaScript 有著非常緊密的關係。
其中一個最關鍵的特色就是:你可以在 JSX 裡用一對大括號 {}
來插入 JavaScript 表達式,讓畫面根據資料動態改變!
什麼是「JavaScript 表達式」?
簡單來說,「表達式(expression)」就是可以計算出一個值的程式碼。舉例來說:
類型 | 範例 | 備註 |
---|---|---|
變數 | name | 回傳變數的值 |
運算式 | a + b | 計算結果是一個值 |
條件運算 | isLoggedIn ? "歡迎" : "請登入" | 根據條件產生不同值 |
函式呼叫 | getUserName() | 回傳值會顯示在畫面上 |
這些都是「表達式」,也就是你可以安全地放進 JSX 中 {}
的內容。
✅ 如何插入變數?
你可以直接將變數插入到 JSX 裡的標籤中,像這樣:
const name = "Tom"
return <h1>Hello, {name}</h1>
📌 name
是一個變數,放在 {}
裡表示要插入它的值。這段程式會輸出:
<h1>Hello, Tom</h1>
✅ 如何插入運算式、條件判斷?
你也可以在 {}
裡使用三元運算子(ternary operator)做條件渲染:
const isLoggedIn = true
return (
<p>{isLoggedIn ? "歡迎回來" : "請先登入"}</p>
)
📌 如果 isLoggedIn
為 true
,則顯示「歡迎回來」;否則顯示「請先登入」。
✅ 還可以做哪些事?
你可以用 {}
做任何「單行」的 JavaScript 計算或邏輯操作,例如:
<h2>總金額:{price * quantity} 元</h2>
<p>現在時間:{new Date().toLocaleTimeString()}</p>
不能放什麼?
雖然 {}
可以插入 JavaScript 表達式,但有一些東西是不能放進去的,會造成語法錯誤或邏輯錯誤:
🚫 控制語句(statements)
// 錯誤寫法
<p>{if (isLoggedIn) { return "Hi" }}</p>
控制語句像是 if
、for
、while
等,屬於「程式流程控制語句」,不是表達式,不能放在 {}
裡。
🚫 多行程式碼區塊
// 錯誤寫法
<p>{
const name = "Tom"
name.toUpperCase()
}</p>
這樣的多行語句會讓 JSX 編譯器無法判斷你的意圖,因為 {}
只接受「一行能產出值的表達式」。
✅ 正確處理方式:把邏輯寫在 JSX 外
如果你需要條件判斷、變數處理等邏輯,請先寫在 JSX 外面,然後只在 JSX 中插入最終結果:
let message
if (isLoggedIn) {
message = "歡迎回來"
} else {
message = "請先登入"
}
return <p>{message}</p>
或者使用三元運算子(表達式)直接寫在 JSX 裡:
<p>{isLoggedIn ? "歡迎回來" : "請先登入"}</p>
總結一下
可用在 JSX {} 中的 | ❌ 不可用的 |
---|---|
變數 | if、for、while 等語句 |
運算式 | 多行邏輯區塊或大括號程式碼 |
函式呼叫 | 任何無法直接產出值的語句 |
三元運算子 | – |
結語:記住這 5 點,新手錯誤不再犯!
雖然 JSX 看起來像 HTML,但它本質上是 JavaScript 的延伸,所以寫法上有許多不一樣的地方。
只要記住以下 5 點,就能大幅減少踩雷機會:
- 用
className
取代class
style
寫成物件 + 駝峰命名- 每個 return 只能有一個根元素
- 所有屬性與事件皆需駝峰命名
- 用
{}
插入 JavaScript 表達式(不是語句)
學會這些小技巧,你就能更順利地掌握 React 的基礎,讓寫元件變得更有信心!