iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
JavaScript

JavaScript為什麼筆記本系列 第 10

Day 10|把 Header、Footer 拆成不同的 JSX

  • 分享至 

  • xImage
  •  

Day 9 我們一路追到:

index.html
↓
main.jsx
↓
App.jsx
↓
畫面

如果只看到這裡,很容易有一個錯覺:

所以 React 以後就是全部寫在 App.jsx 裡?

實務上通常不會。

今天不從 JavaScript 語法表開始背,我們直接從一個大家都看過的網站結構開始:

Header
主要內容
Footer

先做一個很普通的公司網站。


如果全部塞進 App.jsx 呢?

最直覺可以這樣寫:

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,很快就會變成一大坨。

所以實務上我們會開始拆。


先把 Header 拆出去

專案先變成:

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 才能做畫面。


App.jsx 怎麼把 Header 拿進來?

回到:

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

Footer 也拆出去

新增:

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 物件。

props:component 收到的一包資料

原本的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 還會收到更多資料

真正的 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>}

比較像:

條件成立才出現,不成立就不要顯示。


實務上還常看到 props 被直接拆開

目前我們寫:

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

先這樣,先顧寶寶去...


上一篇
Day 09|Vite 啟動之後,瀏覽器到底怎麼找到 App.jsx?
下一篇
# Day 11|用map()把Array加工變成畫面,選單10個不用寫10次
系列文
JavaScript為什麼筆記本20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言