JSX 根本就不是在 JavaScript 中寫 HTML
很多人初學 React 時,會覺得 JSX 看起來像是在 JavaScript 裡面寫 HTML,因為它的語法長得很像 HTML 標籤。但這是一個誤解!JSX 其實是一種語法糖(syntactic sugar),它並不是真正的 HTML,而是被設計來讓開發者以類似 HTML 的方式來描述 UI 結構,最終會被轉譯成純粹的 JavaScript 程式碼。
具體來說:
-
JSX 是 JavaScript 的語法擴展(JavaScript XML),允許你在 JavaScript 中撰寫類似 HTML 的結構。
-
它最終會被工具(例如 Babel)轉譯成
React.createElement()
函數呼叫,這些函數會生成 React 元素(虛擬 DOM 的基礎)。 -
例如,下面這段 JSX:
const element = <h1>Hello, world!</h1>;
會被轉譯成:
const element = React.createElement("h1", null, "Hello, world!");
這表示 JSX 只是讓程式碼看起來更直觀,但本質上它還是 JavaScript。因此,JSX 不是 HTML,而是用來描述 UI 的 JavaScript 物件結構。
什麼是 JSX 語法
JSX 語法的定義
JSX 是 React 生態系統中用來描述使用者介面(UI)的語法。它結合了 JavaScript 和類似 HTML 的標籤語法,讓開發者可以用更直觀的方式來定義元件的結構。
JSX 的特點
-
類似 HTML 的語法:
-
JSX 使用類似 HTML 的標籤來定義 UI,例如
<div>
、<h1>
、<button>
等。 -
你也可以自訂元件,例如
<MyComponent />
。 -
範例:
function App() {
return (
<div>
<h1>我的第一個 React 應用</h1>
<p>這是 JSX 的範例</p>
</div>
);
}
-
-
嵌入 JavaScript 表達式:
-
在 JSX 中,可以使用大括號 嵌入 JavaScript 表達式。
-
範例:
const name = "小明";
const element = <h1>你好,{name}!</h1>;
-
-
屬性使用駝峰命名:
-
JSX 的屬性使用駝峰命名法(camelCase),而不是 HTML 的連字符(kebab-case)。
-
例如,HTML 的 class 在 JSX 中是 className,onclick 是 onClick。
-
範例:
const element = (
<div className="container" onClick={handleClick}>
點我
</div>
);
-
-
必須有一個根元素:
-
JSX 必須回傳單一的根元素。如果有多個元素,必須用一個父元素(或
<></>
片段)包起來。 -
範例:
return (
<>
<h1>標題</h1>
<p>內容</p>
</>
);
-
-
不是 HTML,而是 JavaScript:
- JSX 最終會被轉譯成 React.createElement(),這是 React 用來建立虛擬 DOM 的方式。
為什麼需要 JSX?
-
提高可讀性:相較於直接撰寫 React.createElement(),JSX 讓程式碼更直觀,像寫 HTML 一樣。
-
結構化 UI:讓開發者能以宣告式的方式描述 UI,而不是用命令式的方式操作 DOM。
-
與 JavaScript 無縫整合:可以直接在 JSX 中使用 JavaScript 的邏輯和變數。
以 Babel 來進行 JSX 語法的轉譯
什麼是 Babel?
Babel 是一個 JavaScript 編譯器,廣泛用於將現代 JavaScript(或 JSX)轉譯成瀏覽器能理解的舊版 JavaScript 程式碼。對於 JSX,Babel 的主要工作是將 JSX 語法轉譯成 React.createElement() 函數呼叫。
JSX 轉譯的過程
-
原始 JSX 程式碼:
const element = <h1 className="greeting">Hello, world!</h1>;
-
Babel 轉譯後的結果: Babel 會將上述 JSX 轉譯成:
const element = React.createElement(
"h1",
{ className: "greeting" },
"Hello, world!"
); -
轉譯的意義:
-
React.createElement() 會生成一個描述 UI 的 JavaScript 物件(React 元素)。
-
這個物件最終會被 React 用來渲染到實際的 DOM 上。
-
如何設定 Babel 來轉譯 JSX?
以下是設定 Babel 的詳細步驟,假設你正在使用一個 React 專案:
步驟 1:初始化專案
如果你還沒有 React 專案,可以使用 Create React App 快速建立:
npx create-react-app my-app
cd my-app
npm start
Create React App 已經內建 Babel,無需額外設定。
步驟 2:手動設定 Babel(如果需要)
若你從頭開始建置專案,需自行安裝 Babel:
-
安裝必要的套件:
npm install --save-dev @babel/core @babel/preset-react
-
建立 Babel 設定檔(babel.config.json):
{
"presets": ["@babel/preset-react"]
} -
編譯 JSX 檔案:
-
使用 Babel CLI 將 JSX 檔案轉譯成 JavaScript:
npx babel src --out-dir dist --presets=@babel/preset-react
-
這會將 src 資料夾中的 JSX 檔案轉譯到 dist 資料夾。
-
步驟 3:確認 React 環境
確保你的程式碼中已引入 React,因為 JSX 轉譯後會依賴 React.createElement:
import React from "react";
注意事項
-
需要 React:在舊版 React 中,必須在每個 JSX 檔案中引入 React,即使你沒直接使用它(因為轉譯後會用到 React.createElement)。
-
Babel 設定:確保 @babel/preset-react 已正確安裝,否則 JSX 無法被轉譯。
新版 JSX transformer 與 jsx-runtime
什麼是新版 JSX Transformer?
從 React 17 開始,React 引入了新的 JSX 轉譯方式(JSX Transform),讓 JSX 的使用更簡單,無需在每個檔案中顯式引入 React。這是由 @babel/preset-react 中的 automatic 模式實現的。
舊版 vs. 新版 JSX 轉譯
-
舊版(classic):
-
JSX 會被轉譯成 React.createElement()。
-
每個 JSX 檔案都需要引入 React:
import React from "react";
function App() {
return <h1>Hello, world!</h1>;
}
-
-
新版(automatic):
-
JSX 會被轉譯成由 @babel/preset-react 提供的 jsx 或 jsxs 函數(來自 react/jsx-runtime)。
-
不需要顯式引入 React。
-
範例:
// 不需要 import React
function App() {
return <h1>Hello, world!</h1>;
} -
轉譯後的程式碼:
import { jsx as _jsx } from "react/jsx-runtime";
function App() {
return _jsx("h1", { children: "Hello, world!" });
}
-
如何啟用新版 JSX Transform?
-
確保 React 版本:
-
使用 React 17 或更高版本。
-
檢查 package.json 中的 react 和 react-dom 版本:
"react": "^17.0.0",
"react-dom": "^17.0.0"
-
-
更新 Babel 設定:
-
編輯 babel.config.json,設定 runtime: "automatic":
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}
-
-
移除不必要的 React 引入:
- 在所有 JSX 檔案中,移除 import React from 'react',因為新版轉譯會自動引入 react/jsx-runtime。
jsx-runtime 是什麼?
-
react/jsx-runtime 是 React 17 引入的一個模組,提供了 jsx 和 jsxs 函數,用來取代 React.createElement。
-
優點:
-
減少程式碼量(無需引入 React)。
-
提升效能(更優化的轉譯方式)。
-
與現代打包工具(如 esbuild、Vite)更相容。
-
實務範例
以下是一個完整的 React 元件,使用新版 JSX Transform:
// src/App.jsx
function App() {
const name = "小明";
return (
<div className="container">
<h1>你好,{name}!</h1>
<button onClick={() => alert("點擊了!")}>點我</button>
</div>
);
}
export default App;
轉譯後的程式碼(使用 automatic 模式):
import { jsx as _jsx } from "react/jsx-runtime";
function App() {
const name = "小明";
return _jsx("div", {
className: "container",
children: [
_jsx("h1", { children: ["你好,", name, "!"] }),
_jsx("button", { onClick: () => alert("點擊了!"), children: "點我" }),
],
});
}
export default App;
總結
-
JSX 不是 HTML:它是 JavaScript 的語法糖,轉譯後變成 React.createElement 或 jsx/jsxs 函數呼叫。
-
JSX 語法:提供類似 HTML 的方式來描述 UI,支援 JavaScript 表達式、駝峰命名,且需要單一根元素。
-
Babel 轉譯:Babel 將 JSX 轉成 JavaScript,需安裝 @babel/preset-react,並確保 React 環境正確設定。
-
新版 JSX Transform:React 17 後,透過 automatic 模式和 jsx-runtime,無需引入 React,程式碼更簡潔。