一、使用 JSX 書寫標簽語言
JSX 是一種 JavaScript 的語法擴展,React 使用它來描述用戶界面。
什么是 JSX?
- JSX 是 JavaScript 的一種語法擴展。
- 看起來像 HTML,但它實際上是在 JavaScript 代碼中寫 XML/HTML。
- 瀏覽器并不能直接運行 JSX,需要通過打包工具(如 Babel)將其轉譯為 JavaScript。
示例:
const element = <h1>Hello, world!</h1>;
1、JSX 的基本規則
使用大寫字母定義組件
function MyButton() {return <button>I'm a button</button>;
}
- 小寫字母開頭的標簽,如
<div>
被解析為 HTML 標簽。 - 大寫字母開頭的標簽,如
<MyButton>
被解析為 React 組件。
必須使用閉合標簽
- 所有標簽必須閉合(類似 XML 語法)
// 正確
<input />
<br />
<MyComponent />// 錯誤
<input>
使用 {}
插入 JavaScript 表達式
const user = "小明";
const element = <h1>Hello, {user}!</h1>;
- 只能插入表達式(不是語句)
合法表達式:
{1 + 2}
{user.name}
{formatDate(date)}
非法語句:
{if (isTrue) { ... }}
{for (...) { ... }}
使用 className
代替 class
// HTML 寫法
<div class="container"></div>// JSX 寫法
<div className="container"></div>
因為 class
是 JavaScript 的關鍵字,所以要使用 className
。
使用 camelCase
的屬性名
// HTML 寫法
<input tabindex="0" onclick="handleClick()" />// JSX 寫法
<input tabIndex={0} onClick={handleClick} />
2、條件渲染和列表渲染
條件渲染
使用三元表達式、邏輯與 &&
:
{isLoggedIn ? <LogoutButton /> : <LoginButton />}{messages.length > 0 && <Notification messages={messages} />}
列表渲染
使用 map()
進行循環輸出,并為每個子元素設置唯一的 key
const items = ['A', 'B', 'C'];<ul>{items.map(item => <li key={item}>{item}</li>)}
</ul>
3、JSX 轉換成 JavaScript 的原理
JSX 會被轉譯為 React.createElement
調用:
const element = <h1 className="title">Hello</h1>;// 會被轉換為:
const element = React.createElement('h1', { className: 'title' }, 'Hello');
4、組合 JSX
JSX 支持嵌套結構:
function App() {return (<div><Header /><Content /><Footer /></div>);
}
可使用片段(Fragment)避免多余的 DOM 元素:
<><td>內容1</td><td>內容2</td>
</>
5、JSX Tips
注釋寫法:
{/* 這是注釋 */}
多行 JSX 需要用括號包裹:
return (<div><h1>Hello</h1></div>
);
二、組件(Component)
React 應用是由組件構成的,組件是可以復用的 UI 單元。
什么是組件?
- 組件(Component) 是 React 的核心概念。
- 本質上是一個返回 JSX 的函數。
- 組件名稱必須以大寫字母開頭。
示例:
function MyButton() {return <button>I'm a button</button>;
}
在 JSX 中使用:
export default function MyApp() {return (<div><h1>Welcome to my app</h1><MyButton /></div>);
}
組件命名規則
- 必須以大寫字母開頭,否則會被當成 HTML 標簽。
- 使用 PascalCase 命名約定(每個單詞首字母大寫)。
1、組件是函數,不是標簽
function MyButton() {return <button>Click me</button>;
}
這個 MyButton
是一個函數,而 <MyButton />
是它的使用方式(調用)。
2、組件可以復用
你可以多次使用同一個組件,它們是互相獨立的:
function MyApp() {return (<div><MyButton /><MyButton /></div>);
}
每個 <MyButton />
都會渲染一個獨立的按鈕。
3、組件的結構建議
建議為每個組件建一個文件(例如 MyButton.jsx
),用于項目組織:
src/
├─ components/
│ └─ MyButton.jsx
└─ App.jsx
🧪 示例代碼匯總
// MyButton.jsx
export default function MyButton() {return <button>I'm a button</button>;
}// App.jsx
import MyButton from './MyButton';export default function MyApp() {return (<div><h1>Welcome to my app</h1><MyButton /><MyButton /></div>);
}
三、State
React 的狀態(state)允許組件“記住”信息。狀態是讓組件有“記憶”的機制,通常用于跟蹤用戶交互或界面變化。
1、什么是狀態(State)?
- 狀態是組件的“記憶”。
- 在每次重新渲染時,組件的狀態保持不變。
- 狀態的變化會 觸發組件的重新渲染。
2、如何添加狀態?
通過 useState
Hook:
import { useState } from 'react';function MyComponent() {const [count, setCount] = useState(0);
}
useState
解釋:
const [state, setState] = useState(initialValue);
名稱 | 含義 |
---|---|
state | 當前狀態值 |
setState | 用于更新狀態的函數 |
initialValue | 初始狀態值 |
3、狀態的基本用法示例
import { useState } from 'react';export default function MyButton() {const [count, setCount] = useState(0);function handleClick() {setCount(count + 1);}return (<button onClick={handleClick}>Clicked {count} times</button>);
}
4、每次點擊發生了什么?
- 點擊按鈕時,
handleClick
被調用。 setCount(count + 1)
更新狀態。- React 重新渲染組件。
- 新的
count
顯示在界面上。
狀態更新不會改變當前值,而是觸發一次新的渲染,組件中的
count
會更新為新值。
5、狀態在組件之間是隔離的
每個組件實例有自己獨立的狀態。
<MyButton />
<MyButton />
上面兩個按鈕互不影響,即使它們使用相同的 useState
。
6、不要直接修改 state 變量
// 錯誤寫法(不會觸發重新渲染)
count = count + 1;// 正確寫法
setCount(count + 1);
只有通過 setCount
這樣的更新函數,React 才會觸發重新渲染。
7、多個狀態變量
可以在一個組件中使用多個 useState
:
const [count, setCount] = useState(0);
const [name, setName] = useState('React');
8、示例完整代碼
import { useState } from 'react';function MyButton() {const [count, setCount] = useState(0);function handleClick() {setCount(count + 1);}return (<button onClick={handleClick}>Clicked {count} times</button>);
}export default function MyApp() {return (<div><h1>Welcome to my app</h1><MyButton /><MyButton /></div>);
}
四、響應事件
React 使用類似 HTML 的方式來處理用戶交互事件,比如點擊、輸入、懸停等。但語法略有不同,并支持更強的邏輯功能。
1、事件綁定基礎
React 使用 onClick
、onChange
等屬性來綁定事件處理函數。
示例:
function MyButton() {function handleClick() {alert('你點擊了我!');}return (<button onClick={handleClick}>點擊我</button>);
}
注意:
- 使用駝峰命名(如
onClick
,而不是onclick
) - 事件處理函數是一個普通的 JavaScript 函數
- JSX 中不使用字符串綁定函數(不同于 HTML 的
onclick="handleClick()"
)
2、為什么使用函數名而不是函數調用?
// 正確
onClick={handleClick}// 錯誤(會立即執行)
onClick={handleClick()}
你應傳遞函數的引用,而不是函數的執行結果。
3、使用箭頭函數傳參
有時你希望傳遞參數給事件處理函數,可以使用箭頭函數:
function handleClick(name) {alert(`Hello, ${name}!`);
}<button onClick={() => handleClick('小明')}>Say Hello
</button>
4、在組件中組合事件處理邏輯
React 鼓勵你將組件拆成小塊,事件處理函數可以在組件內部定義或向下傳遞:
function Button({ onClick, children }) {return <button onClick={onClick}>{children}</button>;
}function App() {function handleClick() {alert('Clicked!');}return (<div><Button onClick={handleClick}>按鈕1</Button><Button onClick={handleClick}>按鈕2</Button></div>);
}
5、常見事件類型
React 事件名 | 對應 HTML | 說明 |
---|---|---|
onClick | onclick | 點擊事件 |
onChange | onchange | 輸入/選擇改變 |
onSubmit | onsubmit | 表單提交 |
onMouseEnter | onmouseenter | 鼠標進入 |
onKeyDown | onkeydown | 按鍵按下 |
6、阻止默認行為
可以在事件中調用 event.preventDefault()
:
function handleSubmit(e) {e.preventDefault();alert('提交已阻止');
}<form onSubmit={handleSubmit}><button type="submit">提交</button>
</form>
7、React 與原生 DOM 事件的區別
項目 | React | 原生 HTML |
---|---|---|
命名方式 | 駝峰命名,如 onClick | 小寫,如 onclick |
傳遞方式 | 傳函數引用 | 傳字符串或函數調用 |
自動阻止冒泡 | 否,你仍需手動阻止冒泡 | 同樣需手動處理 |
8、示例完整代碼
function Button({ message, children }) {function handleClick() {alert(message);}return (<button onClick={handleClick}>{children}</button>);
}export default function App() {return (<div><Button message="你好!">點我</Button><Button message="再見!">再點我</Button></div>);
}
五、React 哲學
“React 哲學” 教你如何從 UI 設計圖開始,一步步將頁面拆解為組件,再構建出數據驅動的交互式界面。
示例場景簡介
我們要實現一個可搜索的商品表格(Searchable Product Table),它包含:
- 一個搜索框
- 一個是否只顯示有庫存商品的勾選框
- 一個根據品類分組的商品表格
構建步驟總覽
React 官方建議采用 五步法:
- 將 UI 拆解為組件層級結構
- 構建組件的靜態版本(無交互)
- 確定最小但完整的 UI 狀態表示
- 確定哪些組件擁有狀態(狀態提升)
- 添加反向數據流(處理用戶輸入)
1、第一步:將 UI 拆解為組件層級
觀察 UI,并根據界面結構拆出以下組件:
組件層級結構
FilterableProductTable (父組件)
├─ SearchBar
└─ ProductTable├─ ProductCategoryRow└─ ProductRow
每個組件的職責
組件名 | 作用描述 |
---|---|
FilterableProductTable | 管理所有狀態,整合其他組件 |
SearchBar | 輸入搜索文本與是否過濾庫存 |
ProductTable | 接收數據與過濾條件,渲染表格 |
ProductCategoryRow | 顯示每個品類的標題 |
ProductRow | 顯示單個商品 |
2、第二步:構建靜態版本(無交互)
- 使用父組件
FilterableProductTable
將假數據通過 props 傳給子組件。 - 每個組件只關注如何顯示數據,不包含狀態或交互。
- 假數據示例:
const PRODUCTS = [{category: "Fruits", price: "$1", stocked: true, name: "Apple"},{category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},{category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},{category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},{category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},{category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];
3、第三步:確定最小但完整的 UI 狀態(State)
根據界面交互功能,確定需要驅動 UI 的狀態:
- 搜索文本(
filterText
) - 是否只顯示有庫存商品(
inStockOnly
)
不是狀態的內容(可由 props 或其他狀態推導得出):
- 商品數據(是靜態的)
- 分類標題(可從數據中提取)
- 篩選后的商品列表(由
filterText
+inStockOnly
計算)
4、第四步:決定狀態的歸屬
狀態應該放在最“靠上的共同祖先組件”中。
狀態名 | 所屬組件 | 原因 |
---|---|---|
filterText | FilterableProductTable | SearchBar 和 ProductTable 都使用它 |
inStockOnly | FilterableProductTable | 同上 |
5、第五步:添加反向數據流(提升狀態 + 子傳父)
讓 SearchBar
接收 filterText
和 inStockOnly
作為 props,并通過 onChange
回調將用戶輸入傳遞給父組件修改狀態。
function SearchBar({ filterText, inStockOnly, onFilterTextChange, onInStockChange }) {return (<form><inputtype="text"value={filterText}onChange={(e) => onFilterTextChange(e.target.value)}placeholder="Search..."/><label><inputtype="checkbox"checked={inStockOnly}onChange={(e) => onInStockChange(e.target.checked)}/>Only show products in stock</label></form>);
}
6、組件結構(最終)
<FilterableProductTable products={PRODUCTS} />// 內部包含└── <SearchBarfilterText={...}inStockOnly={...}onFilterTextChange={...}onInStockChange={...}/>└── <ProductTableproducts={...}filterText={...}inStockOnly={...}/>
學習資料來源
使用 JSX 書寫標簽語言
第一個組件
State
響應事件
React 哲學