React 入門

本文將帶你初識 React。我們將瞭解其背景和用例,在本地計算機上設定基本的 React 工具鏈,並建立一個簡單的入門應用並進行操作——在此過程中,學習 React 的工作原理。

先決條件

熟悉核心 HTMLCSSJavaScript 語言,瞭解 終端/命令列

React 使用一種名為 JSX(JavaScript 和 XML)的 HTML-in-JavaScript 語法。熟悉 HTML 和 JavaScript 將有助於你學習 JSX,並更好地識別應用程式中的錯誤是與 JavaScript 相關,還是與 React 的更具體領域相關。

目標

設定本地 React 開發環境,建立入門應用,並瞭解其工作原理的基礎知識。

Hello React

正如其官方標語所述,React 是一個用於構建使用者介面的庫。React 不是一個框架——它甚至不侷限於 Web。它與其他庫一起用於渲染到某些環境中。例如,React Native 可用於構建移動應用程式。

為了構建 Web 應用,開發者將 React 與 ReactDOM 結合使用。React 和 ReactDOM 通常在與其他真正的 Web 開發框架相同的空間中討論——並用於解決相同的問題。當我們將 React 稱為“框架”時,我們是在使用這種口語化的理解。

React 的主要目標是最小化開發人員在構建 UI 時發生的錯誤。它透過使用元件來實現這一點——元件是描述使用者介面一部分的自包含的邏輯程式碼片段。這些元件可以組合在一起以建立完整的 UI,而 React 抽象掉了大部分渲染工作,使你能夠專注於 UI 設計。

用例

與本模組中介紹的其他框架不同,React 不會對程式碼約定或檔案組織強加嚴格的規則。這允許團隊設定最適合他們的約定,並以他們希望的任何方式採用 React。React 可以處理單個按鈕、介面的一些片段或應用程式的整個使用者介面。

雖然 React 可以用於 介面的小部分,但它不像 jQuery 這樣的庫或 Vue 這樣的框架那樣容易“插入”應用程式——當你使用 React 構建整個應用程式時,它更容易上手。

此外,React 應用程式的許多開發者體驗優勢(例如使用 JSX 編寫介面)都需要編譯過程。向網站新增像 Babel 這樣的編譯器會降低其上的程式碼執行速度,因此開發人員通常會透過構建步驟來設定此類工具。React 可以說是對工具的要求很高,但它是可以學習的。

本文將重點介紹使用 React 渲染應用程式整個使用者介面,並藉助 Vite(一種現代前端構建工具)的用例。

React 如何使用 JavaScript?

React 利用現代 JavaScript 的許多特性來實現其許多模式。它與 JavaScript 最大的區別在於使用了 JSX 語法。JSX 擴充套件了 JavaScript 的語法,以便 HTML 類程式碼可以與其並存。例如

jsx
const heading = <h1>Mozilla Developer Network</h1>;

此標題常量被稱為**JSX 表示式**。React 可以使用它在我們的應用程式中渲染該 <h1> 標籤。

假設出於語義原因,我們想將標題包裝在 <header> 標籤中?JSX 方法允許我們將元素彼此巢狀,就像我們在 HTML 中所做的那樣

jsx
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);

注意:前面程式碼片段中的括號不是 JSX 獨有的,也不會對你的應用程式產生任何影響。它們是向你(以及你的計算機)發出的訊號,表明內部的多行程式碼是同一個表示式的組成部分。你也可以這樣編寫 header 表示式

jsx
const header = <header>
  <h1>Mozilla Developer Network</h1>
</header>;

但是,這看起來有點彆扭,因為開始表示式的 <header> 標籤沒有縮排到與其對應的結束標籤相同的位置。

當然,你的瀏覽器在沒有幫助的情況下無法讀取 JSX。當編譯(使用像 BabelParcel 這樣的工具)時,我們的 header 表示式將如下所示

jsx
const header = React.createElement(
  "header",
  null,
  React.createElement("h1", null, "Mozilla Developer Network"),
);

可以跳過編譯步驟並使用 React.createElement() 自己編寫 UI。但是,這樣做會失去 JSX 的宣告性優勢,並且你的程式碼變得難以閱讀。編譯是開發過程中的一個額外步驟,但 React 社群中的許多開發人員認為 JSX 的可讀性是值得的。此外,現代前端開發幾乎總是涉及構建過程——你必須將現代語法降級以與舊版瀏覽器相容,並且你可能希望 壓縮 程式碼以最佳化載入效能。像 Babel 這樣的流行工具已經開箱即用地支援 JSX,因此除非你想要,否則不必自己配置編譯。

由於 JSX 是 HTML 和 JavaScript 的混合體,因此一些開發人員發現它很直觀。其他人則表示其混合性質使其令人困惑。但是,一旦你對它感到滿意,它將使你能夠更快、更直觀地構建使用者介面,並使其他人能夠一目瞭然地更好地理解你的程式碼庫。

要了解有關 JSX 的更多資訊,請檢視 React 團隊的 使用 JSX 編寫標記 文章。

設定你的第一個 React 應用

建立新的 React 應用程式的方法有很多。我們將使用 Vite 透過命令列建立一個新的應用程式。

可以透過 將 React 新增到現有專案 中,方法是將一些 <script> 元素複製到 HTML 檔案中,但使用 Vite 將使你能夠花費更多時間構建應用程式,而花費更少的時間處理設定。

要求

為了使用 Vite,你需要安裝 Node.js。從 Vite 5.0 開始,至少需要 Node 18 或更高版本,並且在能夠使用時最好執行最新的長期支援 (LTS) 版本。截至 2023 年 10 月 24 日,Node 20 是最新的 LTS 版本。Node 包括 npm(Node 包管理器)。

要檢查你的 Node 版本,請在終端中執行以下命令

bash
node -v

如果安裝了 Node,你將看到版本號。如果沒有,你將看到錯誤訊息。要安裝 Node,請按照 Node.js 網站 上的說明進行操作。

你可以使用 Yarn 包管理器作為 npm 的替代方案,但我們假設在本套教程中你使用的是 npm。有關 npm 和 yarn 的更多資訊,請參閱 包管理基礎知識

如果你使用的是 Windows,則需要安裝一些軟體才能讓你與 Unix/macOS 終端保持一致,以便使用本教程中提到的終端命令。Gitbash(作為 git for Windows 工具集 的一部分)或Windows 子系統用於 LinuxWSL)都適用。有關這些內容以及終端命令的一般資訊,請參閱 命令列速成課程

還要記住,在完成這些教程時,React 和 ReactDOM 生成的應用程式僅適用於 Firefox、Microsoft Edge、Safari 或 Chrome 等相當現代的瀏覽器。

有關更多資訊,請參閱以下內容

初始化你的應用程式

npm 包管理器附帶一個 create 命令,允許你從模板建立新專案。我們可以使用它從 Vite 的標準 React 模板建立一個新應用。確保你 cd 到你希望應用程式在你的機器上駐留的位置,然後在你的終端中執行以下命令

bash
npm create vite@latest moz-todo-react -- --template react

這將使用 Vite 的 react 模板建立一個 moz-todo-react 目錄。

注意:-- 是必要的,用於將引數傳遞給 npm 命令(如 create),而 --template react 引數告訴 Vite 使用其 React 模板。

如果此命令成功,你的終端將列印一些訊息。你應該看到提示你 cd 到新目錄、安裝應用程式依賴項並在本地執行應用程式的文字。讓我們從這兩個命令開始。在你的終端中執行以下命令

bash
cd moz-todo-react && npm install

該過程完成後,我們需要啟動一個本地開發伺服器來執行我們的應用程式。在這裡,我們將向 Vite 的預設建議新增一些命令列標誌,以便在伺服器啟動時立即在瀏覽器中開啟應用程式,並使用埠 3000。

在你的終端中執行以下命令

bash
npm run dev -- --open --port 3000

伺服器啟動後,你應該會看到一個新的瀏覽器選項卡,其中包含你的 React 應用程式

Screenshot of Firefox MacOS open to localhost:3000, showing an application made from Vite's React template

應用程式結構

Vite 為我們提供了開發 React 應用程式所需的一切。其初始檔案結構如下所示

moz-todo-react
├── README.md
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   └── main.jsx
└── vite.config.js

index.html 是最重要的頂級檔案。Vite 將你的程式碼注入到此檔案中,以便你的瀏覽器可以執行它。在本教程中,你無需編輯此檔案,但你應該更改此檔案中 <title> 元素內部的文字以反映你的應用程式的標題。準確的頁面標題對於可訪問性非常重要。

public 目錄包含將直接提供給瀏覽器的靜態檔案,而不會被 Vite 的構建工具處理。現在,它只包含一個 Vite 徽標。

src 目錄是我們大部分時間將要花費的地方,因為它是應用程式原始碼所在的位置。你會注意到此目錄中的一些 JavaScript 檔案以副檔名 .jsx 結尾。此副檔名對於包含 JSX 的任何檔案都是必需的——它告訴 Vite 將 JSX 語法轉換為瀏覽器可以理解的 JavaScript。src/assets 目錄包含你在瀏覽器中看到的 React 徽標。

package.jsonpackage-lock.json 檔案包含有關我們專案的一些元資料。這些檔案不是 React 應用程式獨有的:Vite 為我們填充了 package.json,而 npm 在我們安裝應用程式依賴項時建立了 package-lock.json。要完成本教程,你根本不需要理解這些檔案。但是,如果你想了解更多資訊,可以閱讀 npm 文件中關於 package.jsonpackage-lock.json 的內容。我們還在 包管理基礎知識 教程中討論了 package.json

自定義我們的 dev 指令碼

在我們繼續之前,你可能希望稍微更改一下 package.json 檔案,以便不必每次執行 npm run dev 時都傳遞 --open--port 標誌。在文字編輯器中開啟 package.json 並找到 scripts 物件。更改 "dev" 鍵,使其如下所示

diff
- "dev": "vite",
+ "dev": "vite --open --port 3000",

有了它,每次執行 npm run dev 時,你的應用程式都將在 https://:3000 上的瀏覽器中開啟。

注意:這裡不需要額外的 --,因為我們直接將引數傳遞給 vite,而不是傳遞給預定義的 npm 指令碼。

探索我們的第一個 React 元件 - <App />

在 React 中,**元件**是一個可重用的模組,它渲染我們整個應用程式的一部分。元件可以是大或小的,但它們通常定義明確:它們服務於單個、明顯的用途。

讓我們開啟 src/App.jsx,因為我們的瀏覽器提示我們編輯它。此檔案包含我們的第一個元件 <App />

jsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.com.tw" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

App.jsx 檔案主要由三個部分組成:頂部的一些 `import` 語句,中間的 App() 函式,以及底部的 `export` 語句。大多數 React 元件都遵循這種模式。

匯入語句

檔案頂部的 import 語句允許 App.jsx 使用在其他地方定義的程式碼。讓我們更仔細地看看這些語句。

jsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

第一個語句從 react 庫匯入 useState 鉤子。鉤子是在元件內部使用 React 功能的一種方式。我們將在本教程的後面詳細討論鉤子。

之後,我們匯入 reactLogoviteLogo。請注意,它們的匯入路徑分別以 .// 開頭,並在末尾以 .svg 副檔名結尾。這告訴我們這些匯入是本地的,引用我們自己的檔案而不是 npm 包。

最後一個語句匯入與我們的 <App /> 元件相關的 CSS。請注意,這裡沒有變數名也沒有 from 指令。這被稱為 `副作用匯入`——它不會將任何值匯入 JavaScript 檔案,但它告訴 Vite 將引用的 CSS 檔案新增到最終的程式碼輸出中,以便它可以在瀏覽器中使用。

App() 函式

在匯入語句之後,我們有一個名為 App() 的函式,它定義了 App 元件的結構。雖然大多數 JavaScript 社群更喜歡使用 `小駝峰命名法` 命名,例如 helloWorld,但 React 元件使用帕斯卡命名法(或大駝峰命名法)變數名,例如 HelloWorld,以明確表明給定的 JSX 元素是 React 元件而不是普通的 HTML 標籤。如果將 App() 函式重新命名為 app(),瀏覽器將丟擲錯誤。

讓我們更仔細地看看 App()

jsx
function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.com.tw" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

App() 函式返回一個 JSX 表示式。此表示式定義了瀏覽器最終呈現到 DOM 的內容。

return 關鍵字下有一段特殊的語法:<>。這是一個 `片段`。React 元件必須返回單個 JSX 元素,而片段允許我們做到這一點,而無需在瀏覽器中渲染任意的 <div>。您將在許多 React 應用程式中看到片段。

export 語句

App() 函式之後還有一行程式碼。

jsx
export default App;

此匯出語句使我們的 App() 函式可供其他模組使用。我們稍後將詳細討論。

繼續到 main

讓我們開啟 src/main.jsx,因為 <App /> 元件正在那裡使用。此檔案是我們應用程式的入口點,最初看起來像這樣。

jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

App.jsx 一樣,該檔案首先匯入執行所需的所有 JS 模組和其他資產。

前兩個語句匯入 ReactReactDOM 庫,因為它們在檔案中後面被引用。匯入這些庫時,我們不編寫路徑或副檔名,因為它們不是本地檔案。事實上,它們在我們的 package.json 檔案中列為依賴項。在學習本課的過程中,請注意這種區別!

然後我們匯入我們的 App() 函式和 index.css,其中包含應用於整個應用程式的全域性樣式。

然後我們呼叫 ReactDOM.createRoot() 函式,該函式定義了應用程式的根節點。它將 DOM 元素作為引數,我們希望在其中渲染 React 應用程式。在本例中,它是 ID 為 root 的 DOM 元素。最後,我們將 render() 方法連結到 createRoot() 呼叫,並將我們想要在根節點內渲染的 JSX 表示式傳遞給它。透過將 <App /> 作為此 JSX 表示式,我們告訴 React 呼叫 App() 函式,該函式在根節點內渲染 App 元件

注意:<App /> 在一個特殊的 <React.StrictMode> 元件內渲染。此元件幫助開發人員捕獲程式碼中的潛在問題。

如果您願意,可以閱讀有關這些 React API 的資訊。

重新開始

在開始構建應用程式之前,我們將刪除 Vite 為我們提供的一些樣板程式碼。

首先,作為一個實驗,更改 App.jsx 中的 `<h1>` 元素,使其顯示“Hello, World!”,然後儲存檔案。您會注意到此更改會立即在瀏覽器中執行的 https://:3000 開發伺服器中呈現。在處理應用程式時請記住這一點。

我們不會使用其餘的程式碼!將 App.jsx 的內容替換為以下內容。

jsx
import "./App.css";

function App() {
  return (
    <>
      <header>
        <h1>Hello, World!</h1>
      </header>
    </>
  );
}

export default App;

練習 JSX

接下來,我們將使用我們的 JavaScript 技能,以便更加舒適地編寫 JSX 和處理 React 中的資料。我們將討論如何向 JSX 元素新增屬性,如何編寫註釋,如何從變數和其他表示式渲染內容,以及如何使用 props 將資料傳遞到元件中。

向 JSX 元素新增屬性

JSX 元素可以具有屬性,就像 HTML 元素一樣。嘗試在 App.jsx 檔案中的 <h1> 元素下方新增一個 <button> 元素,如下所示。

jsx
<button type="button">Click me!</button>

儲存檔案後,您將看到一個帶有“Click me!”字樣的按鈕。該按鈕目前還沒有任何功能,但我們很快就會學習如何嚮應用程式新增互動性。

某些屬性與其 HTML 對應項不同。例如,HTML 中的 class 屬性在 JSX 中轉換為 className。這是因為 class 是 JavaScript 中的保留字,而 JSX 是 JavaScript 的擴充套件。如果要向按鈕新增 primary 類,則可以這樣編寫。

jsx
<button type="button" className="primary">
  Click me!
</button>

JavaScript 表示式作為內容

與 HTML 不同,JSX 允許我們在其他內容旁邊直接編寫變數和其他 JavaScript 表示式。讓我們在 App() 函式上方宣告一個名為 subject 的變數。

jsx
const subject = "React";
function App() {
  // code omitted for brevity
}

接下來,將 <h1> 元素中的“World”替換為 {subject}

jsx
<h1>Hello, {subject}!</h1>

儲存檔案並檢查瀏覽器。您應該會看到渲染的“Hello, React!”。

subject 周圍的花括號是 JSX 語法的另一個特性。花括號告訴 React 我們想要讀取 subject 變數的值,而不是渲染文字字串 "subject"。您可以在 JSX 中的花括號內放置任何有效的 JavaScript 表示式;React 將對其進行評估,並將表示式的結果作為最終內容呈現。以下是一系列示例,其上方的註釋解釋了每個表示式將呈現的內容。

jsx
{/* Hello, React :)! */}
<h1>Hello, {subject + ' :)'}!</h1>
{/* Hello, REACT */}
<h1>Hello, {subject.toUpperCase()}</h1>
{/* Hello, 4! */}
<h1>Hello, {2 + 2}!</h1>

即使 JSX 中的註釋也寫在花括號內!這是因為註釋在技術上也是 JavaScript 表示式。/* 塊註釋語法 */ 是為了讓程式知道註釋的開始和結束位置。

元件 props

Props 是將資料傳遞到 React 元件的一種方式。事實上,它們的語法與屬性相同:prop="value"。不同之處在於,屬性傳遞到普通元素,而 props 傳遞到 React 元件。

在 React 中,資料流是單向的:props 只能從父元件向下傳遞到子元件。

讓我們開啟 main.jsx 併為我們的 <App /> 元件提供第一個 prop。

<App /> 元件呼叫新增一個名為 subject 的 prop,其值為 Clarice。完成後,它應該看起來像這樣。

jsx
<App subject="Clarice" />

回到 App.jsx 中,讓我們重新訪問 App() 函式。更改 App() 的簽名,使其接受 props 作為引數,並將 props 記錄到控制檯以便您可以檢查它。同時刪除 subject 常量;我們不再需要它了。您的 App.jsx 檔案應該如下所示。

jsx
function App(props) {
  console.log(props);
  return (
    // code omitted for brevity
  );
}

儲存檔案並檢查瀏覽器。您將看到一個空白背景,沒有任何內容。這是因為我們試圖讀取一個不再定義的 subject 變數。透過註釋掉 <h1>Hello {subject}!</h1> 行來解決此問題。

注意:如果您的程式碼編輯器瞭解如何解析 JSX(大多數現代編輯器都瞭解!),您可以使用其內建的註釋快捷鍵——Ctrl + /(在 Windows 上)或 Cmd + /(在 macOS 上)——更快地建立註釋。

儲存註釋掉該行的檔案。這次,您應該會看到僅渲染您的“Click me!”按鈕。如果開啟瀏覽器的開發者控制檯,您將看到一條類似於以下內容的訊息。

Object { subject: "Clarice" }

物件屬性 subject 對應於我們新增到 <App /> 元件呼叫的 subject prop,字串 Clarice 對應於其值。React 中的元件 props 始終以這種方式收集到物件中。

讓我們使用此 subject prop 來修復應用程式中的錯誤。取消註釋 <h1>Hello, {subject}!</h1> 行並將其更改為 <h1>Hello, {props.subject}!</h1>,然後刪除 console.log() 語句。您的程式碼應如下所示。

jsx
function App(props) {
  return (
    <>
      <header>
        <h1>Hello, {props.subject}!</h1>
        <button type="button" className="primary">
          Click me!
        </button>
      </header>
    </>
  );
}

儲存後,應用程式現在應該會向您問好“Hello, Clarice!”。如果您返回 main.jsx,編輯 subject 的值並儲存,您的文字將發生更改。

為了進行額外的練習,您可以嘗試向 main.jsx 內的 <App /> 元件呼叫新增額外的 greeting prop,並在 App.jsx 內與 subject prop 一起使用。

總結

這標誌著我們對 React 初步瞭解的結束,包括如何在本地安裝它,建立啟動應用程式以及基本工作原理。在下一篇文章中,我們將開始構建我們的第一個正式應用程式——一個待辦事項列表。但是,在此之前,讓我們回顧一下我們學到的一些內容。

在 React 中

  • 元件可以匯入它們所需的模組,並且必須在檔案底部匯出自身。
  • 元件函式使用 PascalCase 命名。
  • 您可以透過將 JavaScript 表示式放在花括號之間來渲染它們,例如 {so}
  • 某些 JSX 屬性與 HTML 屬性不同,以避免與 JavaScript 保留字衝突。例如,HTML 中的 class 在 JSX 中轉換為 className
  • Props 就像元件呼叫中的屬性一樣編寫,並傳遞到元件中。

另請參閱

學習 React MDN 課程合作伙伴

Scrimba 的 學習 React 課程是終極的 React 101——任何 React 初學者的完美起點。透過解決 140 多個互動式編碼挑戰和構建八個有趣的專案來學習現代 React 的基礎知識。