一個用於構建單頁應用程序 (SPA) 的開源 JavaScript 函式庫
組件化
將網頁拆分成較小、獨立的組件,提高可讀性和可維護性
虛擬 DOM
創建虛擬 DOM 並與真實 DOM 做對比
只更新不同的部分,提高應用的性能
JSX 語法
React 特有的 JavaScript 語法擴展,可在 JavaScript 中編寫 HTML
Single-Page Application
單頁式應用程式
只需要載入一個頁面,當用戶與應用程式進行互動,會利用 Javascript 動態更新網頁內容
讓使用者始終在同一頁面不需要換頁,提供快速且流暢的使用者體驗
Document Object Model
文件物件模型
HTML 的樹狀結構,讓程式可以存取並改變 HTML
透過 DOM,JavaScript 能夠讀取與修改網頁內容,讓使用者與網頁互動
React 專案
npm create vite@latest
執行成功,檔案夾中應有
起動 & 停止開發伺服器
終端輸入
瀏覽器中貼上網址
http://localhost:5173/
終端輸入 Ctrl + C
cd 專案名稱
npm install
npm run dev
修改網頁
修改 src/App.jsx
中的內容
儲存後會自動刷新網頁
安裝後在 Chrome 開發者工具 F12 中
多出「Components ⚛」和「Profiler ⚛」
可查看 React 組件的層級結構
可編輯組件的 props 和 state
專為 快 速開發設計的新型前端建置工具
支援 React、Vue、Svelte 等多種前端框架
JavaScript XML
<h1>Hello, world!</h1>
JSX
JS
const element = <h1>Hello, world!</h1>;
const element = document.createElement('h1');
// 中間可能有其他(亂七八糟的)程式碼
element.textContent = 'Hello, world!';
JSX 的語法較簡潔易讀
<div className="test">
<h1>Hello</hi>
<p>This is just a test page!</p>
</div>
React.createElement(
"div",
{ className: "test" },
React.createElement("h1", null, "Hello"),
React.createElement("p", null, "This is just a test page!")
);
React 會處理+渲染
對應
嵌入 JavaScript 表達式的常見位置:
1. 元素內容
2. 屬性值
const name = "React";
const element = <h1>Hello, {name}!</h1>;
const videoUrl = "https://youtu.be/dQw4w9WgXcQ?si=aQXMJMbKnZDvqpuZ";
const element = <a href={videoUrl}>影片</a>;
class
CSS – 可重複使用的選擇器
JavaScript – 保留字,用於建立物件的模板
JSX 作為一個將兩者混合的語言
將 CSS 的 class 改為 className
避免與 JS 保留字產生衝突
<div className="container"></div>
將 CSS 寫在 HTML 標籤內
style = {style}
color
、font-size
⭢ fontSize
"blue"
、20px
const style = { color: "blue", fontSize: "20px" };
<div style={style}>Hello</div>
同一個組件可使用在不同地方
避免重複程式碼
增加可維護性
組件可嵌套、組合
組成更複雜的頁面
結構更直觀
模塊獨立易於修改
每個組件只負責完成一項功能
職責清晰便於
擴展、測試、維護
組件放在 components 中
(src/components)
方便管理
src/
├── components/
│ ├── common/
│ │ ├── Button.jsx
│ │ ├── InputField.jsx
│ │ └── ...
│ ├── layout/
│ │ ├── Header.jsx
│ │ ├── Footer.jsx
│ │ └── Sidebar.jsx
│ └── pages/
│ ├── HomePage.jsx
│ ├── AboutPage.jsx
│ └── ...
├── App.jsx
├── index.js
└── ...
範例:
App
return
「React 元素」<></>
包住多個容器後回傳export default App;
function App() {
return <h1>hello world</h1>;
}
export default App;
用
<React.Fragment></React.Fragment>
<></>
包裹元素,不會添加多餘的 DOM 節點
btw 講義斜線位置打錯了
return (
<React.Fragment>
<h1>Title</h1>
<p>Content</p>
</React.Fragment>
);
自關閉標籤
<標籤名稱 />
在 HTML 中,某些標籤本來就是 自關閉的(例 <img>
、<br>
)
在 JSX 中,所有沒有子元素的標籤都必須加上 /
來明確表示它是自關閉的
<Square></Square>
// 沒有子元素
<Square />
<img src="logo.png" alt="Logo" />
<input type="text" />
<br />
<hr />
子組件無法修改
父組件傳遞進來的 props
資料從父組件流向子組件
子組件無法傳遞給父組件
props 可以是任何資料類型
例:字串、數字、布林值、陣列、物件、函數⋯⋯
傳遞 props 的寫法類似 HTML 標籤中的屬性
// 父組件傳遞 props:
function ParentComponent() {
return <ChildComponent name="yourName" age={16} />;
}
// 子組件接收 props:
function ChildComponent(props) {
return (
<div>
<p>Name: {props.name}</p>
<p>Age: {props.age}</p>
</div>
);
}
// 解構賦值:將物件中的資料拆開成獨立的變數
function ChildComponent({ name, age }) {
return (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
}
// 利用 props.children 傳遞嵌套的內容
function Wrapper({ children }) {
return <div className="wrapper">{children}</div>;
}
function App() {
return (
<Wrapper>
<h1>Hello, React!</h1>
<p>This is inside the wrapper.</p>
</Wrapper>
);
}
JSX:
HYML:
function handleClick() {
alert("Button clicked!");
}
// 駝峰命名法 onClick
<button onClick={handleClick}>
Click Me
</button>
// 全小寫 onclick
<button onclick="handleClick()">
Click Me
</button>
// 函式() 會立刻執行
return (
<button onClick={handleClick()}>
Click Me
</button>
)
// 如果需要傳遞參數給函式呢?例如:
function showMessage(name) {
alert(`Hello, ${name}!`);
}
// 可以使用箭頭函式
return (
<button onClick={() => showMessage("建北電資")}>
Click Me
</button>
)
表單輸入值
用戶互動
計數器
等等
當 state 改變時
React 會重新渲染對應的 UI
組件之間的 state 不會互相影響
function Counter() {
let count = 0;
return (
<div>
<p>Count: {count}</p>
<button onClick={() => count = count + 1}>Increment</button>
</div>
);
}
export default Counter;
function Counter() {
let count = 0;
console.log("點擊前:", count);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => {
count = count + 1;
console.log("點擊後:", count);
}}>Increment</button>
</div>
);
}
useState
返回一個包含兩個元素的陣列:
count
)
setCount
)
使用 setCount
更新 count
時
組件會重新渲染
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0); // 初始值為 0
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Hooks | 用途 |
---|---|
useState | 狀態管理 |
useEffect | 副作用處理 |
useContext | 全局狀態管理 |
useMemo | 記憶化計算,提高效能 |
在組件渲染後執行副作用
例如:
抓取 API 資料
訂閱事件
手動操作 DOM
設定與清除計時器
import { useState, useEffect } from "react";
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
console.log("計時開始...");
const interval = setInterval(() => {
setSeconds((prev) => prev + 1);
}, 1000);
// 清除計時器,組件卸載後停止運作
return () => {
console.log("計時器清除");
clearInterval(interval);
};
}, []); // 空陣列代表只在組件掛載時執行一次
return <p>計時:{seconds} 秒</p>;
}
export default Timer;
setInterval()
setSeconds(prev => prev + 1)
clearInterval(interval)
清除計時器
停止計時器:clearInterval
解除事件監聽:removeEventListener
讓組件之間共享狀態,避免層層傳遞 props
import { useState, createContext, useContext } from "react";
// 1. 建立 Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState("light");
return (
// 2. 提供 Context 值
<ThemeContext.Provider value={theme}>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
切換主題
</button>
<ChildComponent />
</ThemeContext.Provider>
);
}
function ChildComponent() {
// 3. 在子組件中使用 Context
const theme = useContext(ThemeContext);
return <p>目前的主題是:{theme}</p>;
}
export default App;
createContext()
建立全局狀態ThemeContext.Provider
提供 theme
狀態值useContext(ThemeContext)
讓子組件可以直接存取 theme
useMemo
會記住計算結果
當計算成本較高時,可避免每次渲染都重算一次
import { useState, useMemo } from "react";
function ExpensiveCalculation({ number }) {
const squared = useMemo(() => {
console.log("計算平方中...");
return number * number;
}, [number]); // useMemo 只有當 number 變更時才重新執行計算
return <p>平方結果:{squared}</p>;
}
function App() {
const [num, setNum] = useState(0);
return (
<div>
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
<ExpensiveCalculation number={num} />
</div>
);
}
export default App;
useMemo(() => 計算內容, [依賴變數])
if (條件一) {
若條件一為 true,執行此區塊內的程式碼
}
else if (條件二) {
若條件一為 false,但條件二為 true,執行此區塊內的程式碼
}
else {
若以上所有條件皆為 false,執行此區塊內的程式碼
}
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome Back!</h1>;
}
else {
return <h1>Please Log In</h1>;
}
}
export default Greeting;
// 三元運算子
條件 ? 若條件為 true,返回此值 : 若條件為 false,返回此值
// 巢狀三元運算子
條件一 ?
若條件一為 true,返回此值
:
條件二 ?
若條件一為 false、條件二為 true,返回此值
:
若條件一為 false、條件二為 false,返回此值
function Greeting({ isLoggedIn }) {
return (
<h1>{isLoggedIn ? "Welcome Back!" : "Please Log In"}</h1>
);
}
export default Greeting
// &&
條件 && 若條件為 true,返回此值
// 若左側為 false,返回左側(false 不渲染)
// ||
值 || 預設值
// 若左側為 false,返回右側;若左側為 true,返回左側
// 左側為 false -> false、0、""、null、undefined、NaN
function Message({ unreadMessages }) {
return (
<div>
{unreadMessages.length > 0 && <p>你有 {unreadMessages.length} 則未讀訊息</p>}
</div>
);
}
export default Message;
function Message({ username }) {
return (
<div>
<p>歡迎, {username || "訪客"}!</p>
</div>
);
}
export default Message;
function UserList() {
const users = [
{ name: "小明", score: 90 },
{ name: "小美", score: 88 },
{ name: "小華", score: 60 },
];
return (
<ul>
{users.map((user) => (
<li>
{user.name} - {user.score} 分
</li>
))}
</ul>
);
}
.map()
報錯了⋯⋯
打開 F12
key
屬性
React 識別元素的唯一標識符
幫助 React 確定哪些元素被更改、刪除或新增,提升渲染性能,避免不必要的 DOM 操作
避免使用索引作為 key
:當陣列順序發生變化時可能會出現渲染錯誤
function UserList() {
const users = [
{ id: 1, name: "小明", score: 90 },
{ id: 2, name: "小美", score: 88 },
{ id: 3, name: "小華", score: 60 },
];
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} - {user.score} 分
</li>
))}
</ul>
);
}
react-bootstrap
index.js
或 App.js
中導入:npm install react-bootstrap bootstrap
import "bootstrap/dist/css/bootstrap.min.css";
react-router-dom
npm install react-router-dom
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
function Home() {
return <h2>首頁</h2>;
}
function About() {
return <h2>關於我們</h2>;
}
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
export default App;
Router
Routes
:Route
須包在 Routes
內Route
:
path="/"
:指定 URL 路徑element={<Home />}
:渲染的組件import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
function App() {
return (
<Router>
<nav>
<Link to="/">首頁</Link> | <Link to="/about">關於我們</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
<a>
會重新載入整個頁面
React Router 提供 Link
實現客戶端導航:
import { useNavigate } from "react-router-dom";
function Home() {
const navigate = useNavigate();
return (
<div>
<h2>首頁</h2>
<button onClick={() => navigate("/about")}>前往關於我們</button>
</div>
);
}
在程式中控制跳轉,可以用 useNavigate()
當按下按鈕時,直接跳轉到 /about
頁面
將路由資訊附加到 URL 的 #
符號後面
例如,http://example.com/#/home
當用戶改變路由時,#
後面的路徑會改變
但瀏覽器不會重新加載頁面
觀察本頁的網址
https://slides.com/cloudream/react/#/11/12
#
符號來分隔路由資訊import React from 'react';
import { HashRouter, Route, Link, Switch } from 'react-router-dom';
function App() {
return (
<HashRouter>
<nav>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
<Switch>
<Route path="/home">
<h2>Home Page</h2>
</Route>
<Route path="/about">
<h2>About Page</h2>
</Route>
</Switch>
</HashRouter>
);
}
export default App;
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
function App() {
return (
<Router>
<nav>
<Link to="/">首頁</Link> | <Link to="/about">關於我們</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
my-app
)https://github.com/你的GitHub用戶名/儲存庫名.git
在終端輸入
npm install gh-pages --save-dev
修改 vite.config.js
:
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
base: '/你的儲存庫名/',
plugins: [react()],
})
homepage
屬性:scripts
部分:
package.json
"homepage": "https://你的GitHub用戶名.github.io/儲存庫名"
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
},
"homepage": "https://你的GitHub用戶名.github.io/儲存庫名",
git init
origin
:main
)並推送:
git remote add origin https://github.com/你的GitHub用戶名/儲存庫名.git
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin main
npm run deploy
https://你的GitHub用戶名.github.io/儲存庫名
記得做成發