Skip to main content

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 的特點

  1. 類似 HTML 的語法

    • JSX 使用類似 HTML 的標籤來定義 UI,例如 <div><h1><button> 等。

    • 你也可以自訂元件,例如 <MyComponent />

    • 範例:

      function App() {
      return (
      <div>
      <h1>我的第一個 React 應用</h1>
      <p>這是 JSX 的範例</p>
      </div>
      );
      }
  2. 嵌入 JavaScript 表達式

    • 在 JSX 中,可以使用大括號 嵌入 JavaScript 表達式。

    • 範例:

      const name = "小明";
      const element = <h1>你好,{name}</h1>;
  3. 屬性使用駝峰命名

    • JSX 的屬性使用駝峰命名法(camelCase),而不是 HTML 的連字符(kebab-case)。

    • 例如,HTML 的 class 在 JSX 中是 className,onclick 是 onClick。

    • 範例:

      const element = (
      <div className="container" onClick={handleClick}>
      點我
      </div>
      );
  4. 必須有一個根元素

    • JSX 必須回傳單一的根元素。如果有多個元素,必須用一個父元素(或 <></>片段)包起來。

    • 範例:

      return (
      <>
      <h1>標題</h1>
      <p>內容</p>
      </>
      );
  5. 不是 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 轉譯的過程

  1. 原始 JSX 程式碼

    const element = <h1 className="greeting">Hello, world!</h1>;
  2. Babel 轉譯後的結果: Babel 會將上述 JSX 轉譯成:

    const element = React.createElement(
    "h1",
    { className: "greeting" },
    "Hello, world!"
    );
  3. 轉譯的意義

    • 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:

  1. 安裝必要的套件:

    npm install --save-dev @babel/core @babel/preset-react
  2. 建立 Babel 設定檔(babel.config.json):

    {
    "presets": ["@babel/preset-react"]
    }
  3. 編譯 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 轉譯

  1. 舊版(classic)

    • JSX 會被轉譯成 React.createElement()。

    • 每個 JSX 檔案都需要引入 React:

      import React from "react";
      function App() {
      return <h1>Hello, world!</h1>;
      }
  2. 新版(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?

  1. 確保 React 版本

    • 使用 React 17 或更高版本。

    • 檢查 package.json 中的 react 和 react-dom 版本:

      "react": "^17.0.0",
      "react-dom": "^17.0.0"
  2. 更新 Babel 設定

    • 編輯 babel.config.json,設定 runtime: "automatic":

      {
      "presets": [
      ["@babel/preset-react", {
      "runtime": "automatic"
      }]
      ]
      }
  3. 移除不必要的 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;

總結

  1. JSX 不是 HTML:它是 JavaScript 的語法糖,轉譯後變成 React.createElement 或 jsx/jsxs 函數呼叫。

  2. JSX 語法:提供類似 HTML 的方式來描述 UI,支援 JavaScript 表達式、駝峰命名,且需要單一根元素。

  3. Babel 轉譯:Babel 將 JSX 轉成 JavaScript,需安裝 @babel/preset-react,並確保 React 環境正確設定。

  4. 新版 JSX Transform:React 17 後,透過 automatic 模式和 jsx-runtime,無需引入 React,程式碼更簡潔。