Day 9 我們一路追到:
index.html
↓
main.jsx
↓
App.jsx
↓
畫面
如果只看到這裡,很容易有一個錯覺:
所以 React 以後就是全部寫在
App.jsx裡?
實務上通常不會。
今天不從 JavaScript 語法表開始背,我們直接從一個大家都看過的網站結構開始:
Header
主要內容
Footer
先做一個很普通的公司網站。
最直覺可以這樣寫:
function App() {
return (
<>
<header>
<h1>Cruxilion</h1>
<nav>
首頁|服務|關於我們
</nav>
</header>
<main>
<h2>網站搬家服務</h2>
<p>這裡是主要內容。</p>
</main>
<footer>
<p>© 2026 Cruxilion</p>
</footer>
</>
)
}
export default App
現在還很短,看起來沒什麼問題。
但網站長大之後可能變成:
Header 100 行
首頁 300 行
服務頁 400 行
Footer 80 行
登入功能
會員功能
表單
Modal
導覽列
全部放在 App.jsx,很快就會變成一大坨。
所以實務上我們會開始拆。
專案先變成:
src/
├─ main.jsx
├─ App.jsx
└─ components/
└─ Header.jsx
components 不是 React 規定一定要叫這個名字,只是實務上常用來放可以重複使用的元件。
新增:
src/components/Header.jsx
function Header() {
return (
<header>
<h1>Cruxilion</h1>
<nav>
首頁|服務|關於我們
</nav>
</header>
)
}
export default Header
這裡先抓兩個地方:
function Header() {
跟:
export default Header
白話先讀成:
function Header()
→ 我做了一個叫 Header 的 React component
export default Header
→ 我把 Header 開放給其他檔案使用
其實昨天的 App 也是一個 component:
function App() {
也不是只有 App.jsx 才能做畫面。
回到:
src/App.jsx
加入:
import Header from './components/Header.jsx'
然後:
function App() {
return (
<>
<Header />
<main>
<h2>網站搬家服務</h2>
</main>
</>
)
}
export default App
Day 9 我們看過main.jsx引入App.jsx:
import App from './App.jsx'
今天只是同一件事再發生一次:
import Header from './components/Header.jsx'
檔案關係開始變成:
main.jsx
↓
App.jsx
↓
Header.jsx
<Header /> 是 HTML 嗎?HTML 原本有:
<header>
<footer>
<div>
<h1>
<p>
但 HTML 並沒有:
<Header />
<Header /> 是我們自己做的 React component。
可以先這樣想:
App.jsx 裡出現 <Header />
↓
React 去處理 Header component
↓
Header() return JSX
↓
最後形成真正的畫面
例如:
function Header() {
return (
<header>
<h1>Cruxilion</h1>
</header>
)
}
最後畫面上的 DOM,概念上才會接近:
<header>
<h1>Cruxilion</h1>
</header>
也可以順便注意一個很常見的辨認方式:
<header>
→ 小寫,HTML 元素
<Header />
→ 大寫,我們自己的 React component
新增:
src/components/Footer.jsx
function Footer() {
return (
<footer>
<p>© 2026 Cruxilion</p>
</footer>
)
}
export default Footer
App.jsx 再把它拿進來:
import Header from './components/Header.jsx'
import Footer from './components/Footer.jsx'
function App() {
return (
<>
<Header />
<main>
<h2>網站搬家服務</h2>
<p>這裡是主要內容。</p>
</main>
<Footer />
</>
)
}
export default App
現在專案比較像真的網站:
main.jsx
↓
App.jsx
├─ Header.jsx
├─ main
└─ Footer.jsx
Header:
<h1>Cruxilion</h1>
Footer:
<p>© 2026 Cruxilion</p>
如果哪天公司名稱改了,就要兩邊一起改。
如果二十個 component 都有這個名稱呢?
這時候 JavaScript 的資料才開始真的有用途。
我們可以在 App.jsx 放一份:
const siteName = 'Cruxilion'
先把 const 白話想成:
幫一個值取一個名字,等等可以拿來用。
完整一點:
import Header from './components/Header.jsx'
import Footer from './components/Footer.jsx'
function App() {
const siteName = 'Cruxilion'
return (
<>
<Header siteName={siteName} />
<main>
<h2>網站搬家服務</h2>
</main>
<Footer siteName={siteName} />
</>
)
}
export default App
這兩行:
<Header siteName={siteName} />
<Footer siteName={siteName} />
可以先翻成人話:
Header,我把 siteName 傳給你
Footer,我也把 siteName 傳給你
App 用 <Header siteName={siteName} /> 傳進去的資料,在 Header 端會被 React 收成一個 props 物件。
原本的Header:
function Header() {
return (
<header>
<h1>Cruxilion</h1>
<nav>
首頁|服務|關於我們
</nav>
</header>
)
}
export default Header
改寫成:
function Header(props) {
return (
<header>
<h1>{props.siteName}</h1>
<nav>
首頁|服務|關於我們
</nav>
</header>
)
}
export default Header
這個意思是,siteName是一個props。props是component 收到的一包資料。即使現在只有一個 siteName,它還是 props。
Header 收到的資料,大概就是長這樣:
props = {
siteName: 'Cruxilion'
}
另外其實不一定要寫props,你也可以隨便取他的參數名稱,例如叫做x。
function Header(x) {
return <h1>{x.siteName}</h1>
}
所以:
props.siteName
拿到的就是:
Cruxilion
這就是 React 很常見的資料傳遞:
App.jsx
↓ props
Header.jsx
App.jsx
↓ props
Footer.jsx
{} 這時候就有用了例如:
<h1>{props.siteName}</h1>
這裡的:
{props.siteName}
就是 JSX 走到這裡,再去 JavaScript 裡拿值。
整個展開來看長相是這樣:
先輸出
↓
遇到 {}
↓
執行 props.siteName
↓
拿到 'Cruxilion'
↓
顯示在 裡
所以最後才會顯示:
Cruxilion
這樣就把資料顯示到 component 裡。
真正的 Header 很可能還需要:
const companyName = 'Cruxilion'
const userName = 'Anthony'
const isLoggedIn = true
<Header
siteName={companyName}
userName={userName}
isLoggedIn={isLoggedIn}
/>
Header 收到的資料,概念上像:
{
siteName: 'Cruxilion',
userName: 'Anthony',
isLoggedIn: true
}
這時候網站很常遇到:
有登入
→ 顯示使用者名字
沒登入
→ 顯示「登入」
可以寫:
function Header(props) {
return (
<header>
<h1>{props.siteName}</h1>
<nav>
首頁|服務|關於我們
</nav>
<div>
{props.isLoggedIn ? `Hi, ${props.userName}` : '登入'}
</div>
</header>
)
}
(有注意到嗎?Header 參數(props)、或者說 Header 這個 function 收到一個參數 props,然後我們用props裡面的siteName顯示在的tag,然後用props的isLoggedIn跟props的userName顯示在的tag裡面,也就是說props可以有好幾個,而且可以放在不同位置當不同用途)
這串:
props.isLoggedIn ? `Hi, ${props.userName}` : '登入'
先翻成人話:
有登入嗎?
↓
是 → 顯示 Hi, Anthony
否 → 顯示 登入
這就是 JavaScript 的三元運算子:
條件 ? 成立時 : 不成立時
例如只有登入後才顯示「登出」按鈕:
{props.isLoggedIn && <button>登出</button>}
可以先讀成:
isLoggedIn 是 true
↓
才顯示登出按鈕
所以:
{isLoggedIn ? '會員中心' : '登入'}
比較像:
A、B 二選一。
而:
{isLoggedIn && <button>登出</button>}
比較像:
條件成立才出現,不成立就不要顯示。
目前我們寫:
function Header(props) {
return (
<>
<h1>{props.siteName}</h1>
<p>{props.userName}</p>
</>
)
}
但 React 專案裡很常看到:
function Header({ siteName, userName, isLoggedIn }) {
return (
<header>
<h1>{siteName}</h1>
<div>
{isLoggedIn ? `Hi, ${userName}` : '登入'}
</div>
</header>
)
}
原本的一包:
props = {
siteName: 'Cruxilion',
userName: 'Anthony',
isLoggedIn: true
}
直接拆成:
siteName
userName
isLoggedIn
這就是 JavaScript 的解構 destructuring。
這不是function Header(siteName, userName, isLoggedIn) // ❌ 這是三個參數喔!
解構這個觀念後面還會再遇到,而且會長成一個大家超常看到的東西:
const [count, setCount] = useState(0)
我們從網站真正可能會發生的問題開始看:
App.jsx 不想塞爆
↓
拆 Header / Footer component
↓
import / export
↓
資料怎麼傳進 component?
↓
props
↓
JavaScript 的值怎麼放到 JSX?
↓
{}
↓
不同登入狀態要顯示不同畫面
↓
boolean、? :、&&
原本只是:
index.html
↓
main.jsx
↓
App.jsx
現在開始變成:
main.jsx
↓
App.jsx
├─ Header.jsx
├─ main
└─ Footer.jsx
先這樣,先顧寶寶去...