react
react是啥
渲染使用者介面
React 是用來實作使用者介面的 JavaScript 函式庫(by 官網)
使用 JSX 語法來渲染前端網頁
安裝

注意版本要是 LTS
建立專案
新增資料夾
開一個 terminal
輸入指令
npm create vite@latest 專案名稱 -- --template react
專案名稱記得改成你要的名稱

Ctrl + 左鍵點擊那個藍色連結,就可以開網站了
關閉與開啟網站
關閉:終端機打 ctrl + c
開啟:終端機輸入 npm run dev
注意,輸入前要先確定終端機顯示的路徑正確
路徑的名稱要是你的專案名稱
不然會炸

JSX
html
JSX 讓你能用類似 HTML 的語法來寫網頁
編譯器最後會把這一坨 HTML 轉成 JS
阿為甚麼不直接用 JS 就好
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" 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>
</>
)
}function App() {
const [count, setCount] = useState(0);
return React.createElement(
React.Fragment,
null,
React.createElement(
"div",
null,
React.createElement(
"a",
{ href: "https://vite.dev", target: "_blank" },
React.createElement("img", {
src: viteLogo,
className: "logo",
alt: "Vite logo"
})
),
React.createElement(
"a",
{ href: "https://react.dev", target: "_blank" },
React.createElement("img", {
src: reactLogo,
className: "logo react",
alt: "React logo"
})
)
),
React.createElement(
"h1",
null,
"Vite + React"
),
React.createElement(
"div",
{ className: "card" },
React.createElement(
"button",
{ onClick: () => setCount((count) => count + 1) },
"count is ",
count
),
React.createElement(
"p",
null,
"Edit ",
React.createElement("code", null, "src/App.jsx"),
" and save to test HMR"
)
),
React.createElement(
"p",
{ className: "read-the-docs" },
"Click on the Vite and React logos to learn more"
)
);
}
很明顯 JSX 的易讀性較高,碼量也小
注意
HTML裡面有些標籤是自關閉的,像 <br>
在 JSX 裡面這些自關閉的標籤要加上斜線
像 <br />
檔案架構
寫程式
回到剛剛建好的資料夾
找到 src 這個資料夾
找到 main.jsx 這個檔案
他不是主要寫程式的地方但先看一下裡面的結構
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
main.jsx
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
其中的 <App /> 就是 main.jsx 呼叫 App.jsx 來渲染網頁
好所以來看看 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://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" 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.css
也可以看一下
他就是個正常的 css 檔
strict mode
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
在你之後做成發專案的時候
如果你發現你的一些程式(像 console.log())跑了兩次
那是因為 strictmode 會導致後面提到的 useEffect 執行兩次
阿 strict mode 其實可以拔掉
component
積木
重複利用
你可以把 component 想像成一塊積木
你先在其他地方把一個 component 寫好
然後回到主程式把他 import 進來
現在你就可以把 component 放在好幾個地方
範例
在 src 裡面新增一個資料夾 component
裡面開一個 .jsx 檔
輸入以下程式
function Todo(props) {
return (
<div className="card">
<h2>
{props.title}
</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
);
}
export default Todo;範例
回到 App.jsx
import 剛剛新增的 .jsx 檔(用相對路徑)
把 component 放到 function 裡面
import Todo from './component/Todo';
import "./App.css"
function App() {
return (
<div>
<h1>My Todos</h1>
<Todo title={"No1"} />
<Todo title={"No2"} />
<Todo title={"No3"} />
</div>
);
}
export default App;範例
你就得到了這個介面
耶

講解
function Todo(props) {
return (
<div className="card">
<h2>
{props.title}
</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
);
}
export default Todo;你會發現component 的h2 裡面包了一個 {props.title}
而 App.jsx 裡面在使用 Todo 時給了 title 一個值
這個 props.title 其實就是讓你可以更改的值
而最後這個 "No1" 就會跑到 h2 裡面變成小標題
切記 component 最後要 export,不然網頁會爆炸
import Todo from './component/Todo';
import "./App.css"
function App() {
return (
<div>
<h1>My Todos</h1>
<Todo title={"No1"} />
<Todo title={"No2"} />
<Todo title={"No3"} />
</div>
);
}
export default App;props
剛剛的 props 傳遞的是 string
他其實可以傳遞各種東西
甚至可以傳遞函式
展開
如果不用 component 的話,App.jsx 會長這樣
會有很多重複的地方
import "./App.css"
function App() {
return (
<div>
<h1>My Todos</h1>
<div className="card">
<h2>
No1
</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
<div className="card">
<h2>
No2
</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
<div className="card">
<h2>
No3
</h2>
<div className="actions">
<button className="btn">Delete</button>
</div>
</div>
</div>
);
}
export default App;實作
現在只有一個 title 太少了
給他加多一點東西
例如說截止日期、科目等等
可以嘗試用 css 美化
如果發現網頁甚麼東西都沒有,通常代表有報錯
開 F12 打開 console 就能看到問題
use state
做個計數器
我不知道阿
let count = 0;
function add() {
count = count + 1;
console.log(count);
}執行此程式後, console.log 出來的值當然是 1
但是如果你要用網頁顯示這個變數的話,他還是0
所以 use state 的功用就是叫 react 重新渲染一下畫面
這樣才能看到最新的值,也就是 1
語法
import { useState } from 'react';
const [變數名稱, 修改變數的函式] = useState(初始值);
const [count, setCount] = useState(0);初始化
更改數值
setCount(1);請注意這樣網頁不會重新渲染
count = 1;onclick
這其實是 HTML 自己就有的語法
但 JSX 長得不太ㄧ樣
<button onClick = {changeColor}>Change to Blue</button>當這個按鈕被點擊的時候, changeColor 這個函式就會啟動
注意
這兩行程式差在哪裡呢?
<button onClick = {changeColor()}>Change to Blue</button>
<button onClick = {() => changeColor()}>Change to Blue</button>第一行的函式,是在渲染時就會先叫一次
而第二行的函式,是一個待執行的函式,渲染時不會呼叫
實作
做一個計數器,包含三個按鈕
+1, -1, 歸零
把上個實作的 delete 加個 Onclick 事件
map
不是 C++ 的那個 map
更長的代辦清單
map 可以方便地將一個 list 裡面的值一個個渲染出來
import Todo from './component/Todo';
import "./App.css"
function App() {
return (
<div>
<h1>My Todos</h1>
<Todo title={"No1"} />
<Todo title={"No2"} />
<Todo title={"No3"} />
</div>
);
}
export default App;例如說我想要 10 組 todo
你當然可以一個一個打,但這樣有點笨
創一個 list
這邊的語法跟 JS 基本上是一樣的
let arrLists = []
for (let i = 0 ; i < 10 ; i++) {
arrLists.push("No." + i.toString())
}map語法
{list.map((item, index) => {
return 另一種格式;
})}以下是轉成 Todo 格式的範例
{arrLists.map((item, index) => {
return <Todo key = {index} title={item} />;
})}完整 code
import Todo from './component/Todo';
import "./App.css"
function App() {
let arrLists = []
for (let i = 0 ; i < 10 ; i++) {
arrLists.push("No." + i.toString())
}
return (
<div>
<h1>My Todos</h1>
{arrLists.map((item, index) => {
return <Todo key = {index} title={item} />;
})}
</div>
);
}
export default App;一次 10 個代辦事項
嗚嗚嗚事情好多要似了
use effect
infinite loop
每次渲染時就啟動
在 use effect 中的程式,會在每次元件渲染時啟動
import { useState, useEffect } from 'react';
useEffect (() => {
//程式碼
}, [])範例
例如說我可以在每次重新開啟網頁時,覆蓋 list 的內容
阿當然畫面不會有東西,因為我沒用 use state
import { useState, useEffect } from 'react';
import Todo from './component/Todo';
import "./App.css";
let todo = []
function App() {
useEffect (() => {
todo = []
for (let i = 1 ; i <= 10 ; i++) {
todo.push({id : i, title : "No" + i.toString()})
}
})
return (
<div>
<h1>My Todos</h1>
{todo.map((todo) => (
<Todo
key={todo.id}
title={todo.title}
/>
))}
</div>
);
}
export default App;無限迴圈
我現在把它變成 use state 的樣子
import { useState, useEffect } from 'react';
import Todo from './component/Todo';
import "./App.css";
function App() {
const [todos, setTodos] = useState([
]);
useEffect (() => {
let list = []
for (let i = 1 ; i <= 10 ; i++) {
list.push({id : i, title : "No" + i.toString()})
}
setTodos(list)
})
return (
<div>
<h1>My Todos</h1>
{todos.map((todo) => (
<Todo
key={todo.id}
title={todo.title}
/>
))}
</div>
);
}
export default App;好像沒什麼問題?
打開 console 看看
無限迴圈

怎麼爆炸了
因為你在 useEffect更新陣列後
畫面就會渲染一次
然後又會觸發 useEffect
形成無限迴圈
怎麼辦?
依賴陣列
import { useState, useEffect } from 'react';
useEffect (() => {
//程式碼
}, [])你有看到一個中括號包起來的空陣列嗎
那個就是依賴陣列
當這個依賴陣列沒有產生變動的時候 useEffect 就不會啟動
問題不大
import { useState, useEffect } from 'react';
import Todo from './component/Todo';
import "./App.css";
function App() {
const [todos, setTodos] = useState([
]);
useEffect (() => {
let list = []
for (let i = 1 ; i <= 10 ; i++) {
list.push({id : i, title : "No" + i.toString()})
}
setTodos(list)
}, [])
return (
<div>
<h1>My Todos</h1>
{todos.map((todo) => (
<Todo
key={todo.id}
title={todo.title}
/>
))}
</div>
);
}
export default App;所以就加個 [] 就好了
通靈實作
現在把 delete 按鈕加上刪除功能
這樣能根據 id 刪除指定的值
setTodos(todos.filter((todo) => todo.id !== id));tip1: 這次實作會動到 component 喔
tip2: Todos 在更新後整個 App 函式會重跑一遍
同時 useEffect 不會動(因為依賴陣列沒動)
通靈實作
// component/Todo.js
function Todo(props) {
return (
<div className="card">
<h2>{props.title}</h2>
<div className="actions">
<button className="btn" onClick={props.onDelete}>
Delete
</button>
</div>
</div>
);
}
export default Todo;//App.jsx
import { useState, useEffect } from 'react';
import Todo from './component/Todo';
import "./App.css";
function App() {
const [todos, setTodos] = useState([
]);
function deleteHandler(id) {
setTodos(todos.filter((todo) => todo.id !== id));
}
useEffect (() => {
let list = []
for (let i = 1 ; i <= 10 ; i++) {
list.push({id : i, title : "No" + i.toString()})
}
setTodos(list)
}, [])
return (
<div>
<h1>My Todos</h1>
{todos.map((todo) => (
<Todo
key={todo.id}
title={todo.title}
onDelete={() => deleteHandler(todo.id)}
/>
))}
</div>
);
}
export default App;要在 onClick 上加一個 props
props 裡面也可以塞函式
提醒
return (
<div>
<h1>My Todos</h1>
{todos.map((todo) => (
<Todo
key={todo.id}
title={todo.title}
onDelete={() => deleteHandler(todo.id)}
/>
))}
</div>
);注意到 onDelete 裡面的函式,前面還放了一個 () =>
如果不放的話會發生甚麼事呢?
這個函式會在網頁開啟時就啟動一次
然後把所有的東西刪除
github
pushpushpushpushpush
前置作業
創建 github 帳號並 create repository
不用勾選 Add a README file 或 .gitignore,因為你已經有了
複製 repository 的網址
前置作業
終端機輸入以下指令
npm install gh-pages --save-dev更改 vite.config.js
export default defineConfig({
plugins: [react()],
})
export default defineConfig({
plugins: [react()],
base: "/儲存庫名稱/"
})
更改 package.json
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
},並在最上面添加這串
"homepage": "https://你的GitHub帳號.github.io/你的專案名稱",git
初始化
git initgit add .
git commit -m "Initial commit"加入檔案並提交版本(你會看到專案裡已經有 .gitignore 了所以不用擔心)
連線到 repository
git remote add origin https://github.com/你的帳號/儲存庫名稱.git更改分支名稱,然後把程式碼丟上去
git branch -M main
git push -u origin main
git
初始化
git initgit add .
git commit -m "Initial commit"加入檔案並提交版本(你會看到專案裡已經有 .gitignore 了所以不用擔心)
連線到 repository
git remote add origin https://github.com/你的帳號/儲存庫名稱.git更改分支名稱,然後把程式碼丟上去
git branch -M main
git push -u origin main
啟動
在 terminal 執行
npm run deploy輸入以下網址,應該就可以看到你的網頁了
https://你的GitHub用戶名.github.io/儲存庫名
成發小提醒
舊版本問題
今天講得東西......其實不多,所以你在做成發時理論上需要上網找資料
但請注意,react 有新舊版本之分,有些舊版本的東西拿到現在用會爆炸
講師上次寒訓被這個東西搞
所以如果你發現你上網找的程式是爛的
不一定是你的問題
也不一定是他的問題
可能是版本的問題
記得問問題
然後有問題記得問講師
或著問 AI
丸啦 AI 要取代講師了
Minimal
By ck11300768鄭博軒
Minimal
- 14