React

2025 ckefgisc wintercamp

React 簡介

2025 ckefgisc wintercamp

React

一個用於構建單頁應用程序 (SPA) 的開源 JavaScript 函式庫

 

組件化
將網頁拆分成較小、獨立的組件,提高可讀性可維護性

虛擬 DOM
創建虛擬 DOM 並與真實 DOM 做對比
只更新不同的部分,提高應用的性能

JSX 語法
React 特有的 JavaScript 語法擴展,可在 JavaScript 中編寫 HTML

SPA

Single-Page Application

單頁式應用程式

 

只需要載入一個頁面,當用戶與應用程式進行互動,會利用 Javascript 動態更新網頁內容

讓使用者始終在同一頁面不需要換頁,提供快速且流暢的使用者體驗

DOM

Document Object Model

文件物件模型

 

HTML 的樹狀結構,讓程式可以存取並改變 HTML

透過 DOM,JavaScript 能夠讀取與修改網頁內容,讓使用者與網頁互動

安裝

2025 ckefgisc wintercamp

安裝 VScode

  • VScode

一個程式碼編輯器

1.

安裝 Node.js & npm

  • Node.js

讓 JavaScript 在電腦運行的工具

  • npm

管理 node 的工具

2.

React 專案

  • 用 VScode 開啟一個檔案夾
  • 在終端機輸入
    npm create vite@latest
  • 依提示輸入(用方向鍵選擇)

3.

執行成功,檔案夾中應有

4.

起動 & 停止開發伺服器

  • 啟動開發伺服器

終端輸入

 

 

瀏覽器中貼上網址

http://localhost:5173/

  • 停止開發伺服器

終端輸入 Ctrl + C

5.

cd 專案名稱
npm install
npm run dev

修改網頁

修改 src/App.jsx 中的內容

儲存後會自動刷新網頁

6.

React Developer Tools

安裝後在 Chrome 開發者工具 F12 中

多出「Components ⚛」和「Profiler ⚛」

可查看 React 組件的層級結構

可編輯組件的 props 和 state

Vite

專為 速開發設計的新型前端建置工具

支援 React、Vue、Svelte 等多種前端框架

JSX

JavaScript XML

2025 ckefgisc wintercamp

JSX vs JS

<h1>Hello, world!</h1>

 

JSX

 

JS

const element = <h1>Hello, world!</h1>;
const element = document.createElement('h1');
// 中間可能有其他(亂七八糟的)程式碼
element.textContent = 'Hello, world!';

JSX 的語法較簡潔易讀

React 中定義的 JS 物件

<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>;

className

 class 

 CSS – 可重複使用的選擇器

 JavaScript – 保留字,用於建立物件的模板

 

 JSX 作為一個將兩者混合的語言
CSS 的 class 改為 className
避免與 JS 保留字產生衝突

<div className="container"></div>

style

將 CSS 寫在 HTML 標籤內

  • style = {style}
    屬性 = { 物件 }
  • 鍵 – CSS 屬性名稱(駝峰式命名)
    • 例:colorfont-size ⭢ fontSize
  • 值 – 屬性的對應值
    • 例:"blue"20px
const style = { color: "blue", fontSize: "20px" };
<div style={style}>Hello</div>

Components

2025 ckefgisc wintercamp

Components 組件

可重複使用

可組合

單一功能

同一個組件可使用在不同地方

 

避免重複程式碼
增加可維護性

組件可嵌套、組合
組成更複雜的頁面

 

結構更直觀
模塊獨立易於修改

每個組件只負責完成一項功能

 

職責清晰便於
擴展、測試、維護

組件放在 components 中

(src/components)

方便管理

Components

組件

src/
├── components/
│   ├── common/
│   │   ├── Button.jsx
│   │   ├── InputField.jsx
│   │   └── ...
│   ├── layout/
│   │   ├── Header.jsx
│   │   ├── Footer.jsx
│   │   └── Sidebar.jsx
│   └── pages/
│       ├── HomePage.jsx
│       ├── AboutPage.jsx
│       └── ...
├── App.jsx
├── index.js
└── ...

範例:

函數式組件

  1. 組件:App
    • return 「React 元素」
    • 命名須遵守 PascalCase(首字母大寫)
    • 僅能回傳一個容器
      可用 <></> 包住多個容器後回傳
  2. 導出組件:export default App;
function App() {
  return <h1>hello world</h1>;
}

export default App;

想 return 一堆東西怎麼辦?

Fragment

<React.Fragment></React.Fragment>

<></>

包裹元素,不會添加多餘的 DOM 節點

btw 講義斜線位置打錯了

return (
  <React.Fragment>
    <h1>Title</h1>
    <p>Content</p>
  </React.Fragment>
);

Self-closing Tags

自關閉標籤

<標籤名稱 />

在 HTML 中,某些標籤本來就是 自關閉的(例 <img><br>

在 JSX 中,所有沒有子元素的標籤都必須加上 / 來明確表示它是自關閉的

<Square></Square>
// 沒有子元素
<Square />
<img src="logo.png" alt="Logo" />
<input type="text" />
<br />
<hr />

Props

2025 ckefgisc wintercamp

Props 屬性

不可變

單向資料流

靈活性

子組件無法修改
父組件傳遞進來的 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

// 利用 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>
  );
}

事件處理

2025 ckefgisc wintercamp

事件處理

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

2025 ckefgisc wintercamp

State 狀態

管理動態資料

觸發重新渲染

專屬資料

表單輸入值

用戶互動

計數器

等等

當 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()

useState 返回一個包含兩個元素的陣列:

  1. 當前的 state 值(例:count
  2. 更新 state 的函數(例:setCount

 

使用 setCount 更新 count 時
組件會重新渲染

useState()

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

2025 ckefgisc wintercamp

Hooks 用途
useState 狀態管理
useEffect 副作用處理
useContext 全局狀態管理
useMemo 記憶化計算,提高效能

Hooks

useEffect:處理副作用

在組件渲染後執行副作用

 

例如:

  • 抓取 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;

範例:設定&清除計時器

  1. 設定計時器
    • 每秒執行一次 setInterval()
    • 秒數遞增:setSeconds(prev => prev + 1)
  2. 清除計時器
    • 在組件卸載時執行
    • 透過 clearInterval(interval) 清除計時器

 

停止計時器:clearInterval

解除事件監聽:removeEventListener

useContext:全域狀態管理

讓組件之間共享狀態,避免層層傳遞 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 會記住計算結果

當計算成本較高時,可避免每次渲染都重算一次

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(() => 計算內容, [依賴變數])
    只有在依賴變數變更時才會重新計算
     
  • 避免不必要的計算,提升效能

範例:平方計算

條件渲染

2025 ckefgisc wintercamp

if-else

if (條件一) {
    若條件一為 true,執行此區塊內的程式碼
}
else if (條件二) {
    若條件一為 false,但條件二為 true,執行此區塊內的程式碼
}
else {
    若以上所有條件皆為 false,執行此區塊內的程式碼
}

if-else — 範例

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;

動態列表渲染

2025 ckefgisc wintercamp

動態列表渲染

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 套件

2025 ckefgisc wintercamp

React-Bootstrap

React 套件

React-Bootstrap

  1. 安裝 react-bootstrap

     
  2. 在專案中引入 Bootstrap CSS
    index.js 或 App.js 中導入:


    就能使用 Bootstrap 的樣式了
     
  3. 官方組件列表
npm install react-bootstrap bootstrap
import "bootstrap/dist/css/bootstrap.min.css";

React-Router

React 套件

React-Router

  1. 安裝 react-router-dom
     
npm install react-router-dom

Browser Router

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;

Browser Router

  • Router
    最外層的路由包裹元素
    讓 React 知道要處理路由
  • Routes
    所有的 Route 須包在 Routes 內
  • Route
    • path="/":指定 URL 路徑
    • element={<Home />}:渲染的組件

Browser Router

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 實現客戶端導航:

Browser Router

import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  return (
    <div>
      <h2>首頁</h2>
      <button onClick={() => navigate("/about")}>前往關於我們</button>
    </div>
  );
}

在程式中控制跳轉,可以用 useNavigate()

當按下按鈕時,直接跳轉到 /about 頁面

但 GitHub Pages 拒收這東西

Hash Router

React 套件

Hash Router

將路由資訊附加到 URL 的 # 符號後面

例如,http://example.com/#/home 

當用戶改變路由時,# 後面的路徑會改變

但瀏覽器不會重新加載頁面

 

觀察本頁的網址

https://slides.com/cloudream/react/#/11/12

Hash Router

  • 在 URL 中使用 # 符號來分隔路由資訊
     
  • 伺服器不需要特別配置來處理 URL
     
  • 適合靜態網站,或不需要伺服器端的應用
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>
  );
}

Hash Router

GitHub Pages

2025 ckefgisc wintercamp

  1. 登入你的 GitHub 帳號
  2. 點擊右上角的「+」符號
    選擇「New repository」創建新的儲存庫
  3. 命名儲存庫(例:my-app
    選擇隱私設置(公開或私密)
  4. 創建儲存庫後,可得到儲存庫的 URL:
https://github.com/你的GitHub用戶名/儲存庫名.git

一、創建 GitHub 儲存庫

在終端輸入

二、安裝 GitHub Pages 部署插件

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()],
})
  1. 添加 homepage 屬性:

     
  2. 修改 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/儲存庫名",
  1. 在你的專案的根目錄中打開終端
  2. 執行以下命令,在本地創建新的 Git 儲存庫:

五、初始化本地 Git 儲存庫

git init
  1. 執行以下命令將 GitHub 儲存庫 URL 添加為遠端 origin

     
  2. 新增檔案並提交第一個 commit

     
  3. 設定主分支(main)並推送:

六、將儲存庫推送到 GitHub

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
  1. 設置完成後,部署應用到 GitHub Pages:

     
  2. 完成後,在瀏覽器輸入以下網址查看應用:

七、部署到 GitHub Pages

npm run deploy
https://你的GitHub用戶名.github.io/儲存庫名

記得做成發

END

React

By cloudream

React

  • 115