Day 10 我們終於把網站拆得比較像一個真的 React 專案:
main.jsx
↓
App.jsx
├─ Header.jsx
├─ main
└─ Footer.jsx
Header 裡面可能有:
<nav>
首頁|服務|關於我們
</nav>
三個選單還好。
但如果網站慢慢長大:
首頁
服務
關於我們
部落格
常見問題
聯絡我們
登入
難道我要全部手寫?
<nav>
<a>首頁</a>
<a>服務</a>
<a>關於我們</a>
<a>常見問題</a>
<a>聯絡我們</a>
<a>登入</a>
</nav>
其實可以。
但仔細看會發現,我們一直重複寫的是:
<a>...</a>
真正一直改變的,其實只有裡面的資料。
例如:
<a>首頁</a>
<a>服務</a>
<a>關於我們</a>
可以拆成兩件事情看。
結構是:
<a>...</a>
資料則是:
首頁
服務
關於我們
如果今天公司說:
幫我把「關於我們」移到第二個,然後再加一個「案例分享」。
全部手寫當然還是可以改。
但當選單愈來愈多時,資料跟畫面結構全部黏在一起,就開始不好管理了。
所以我們可以先把資料拿出來。
JavaScript 可以這樣寫:
const menuItems = [
'首頁',
'服務',
'關於我們'
]
這是一個 Array,也就是陣列。
現在先不用背完整定義。
可以先把它理解成:
menuItems
↓
一串有順序的資料
裡面有三筆:
首頁
服務
關於我們
如果想拿第一筆:
menuItems[0]
結果是:
首頁
第二筆:
menuItems[1]
結果是:
服務
JavaScript 很經典的一件事情就是:
第一個位置不是 1
而是 0
所以:
const menuItems = ['首頁', '服務', '關於我們']
可以想成:
[0] 首頁
[1] 服務
[2] 關於我們
現在我們有:
const menuItems = [
'首頁',
'服務',
'關於我們'
]
但瀏覽器最後還是要看到類似:
<a>首頁</a>
<a>服務</a>
<a>關於我們</a>
這時候就碰到 React 裡非常常看到的:
map()
例如:
menuItems.map((item) => {
return item
})
這一串第一次看真的滿像咒語。
我以前看到:
(item) =>
第一反應也是:這箭頭到底哪來的?
箭頭函式後面會再正式拆。
今天先知道:
(item) => {
...
}
是一個 function 就可以了。
map() 到底在幹嘛?假設:
const menuItems = [
'首頁',
'服務',
'關於我們'
]
寫:
menuItems.map((item) => {
return item
})
map() 會把資料一筆一筆拿出來。
概念上:
第 1 次:item = '首頁'
第 2 次:item = '服務'
第 3 次:item = '關於我們'
這裡的:
item
不是整個 menuItems。
它只是「這一輪拿到的那一筆資料」。
然後每一輪都會執行:
return item
所以:
首頁 → return 首頁
服務 → return 服務
關於我們 → return 關於我們
最後 map() 會把每一次 return 的結果收集起來。
結果仍然是一個新的 Array:
[
'首頁',
'服務',
'關於我們'
]
map()這個函式括號裡面的參數可以隨便取。可以叫做fruit,就像是從Array裡面拿出水果。
map() 比較像一台加工機如果只是:
return item
看不太出 map() 有什麼用。
換一個例子。
const prices = [100, 200, 300]
現在全部打八折:
const result = prices.map((price) => {
return price * 0.8
})
它會一筆一筆處理:
100 → 80
200 → 160
300 → 240
最後:
result
就是:
[80, 160, 240]
這時候 map() 的感覺就比較明顯了。
原本每一筆資料
↓
經過某個加工
↓
return 新的東西
↓
全部組成新的 Array
map最後回傳的東西就是一個新的Array。
map() 不會自己加逗號那~
如果結果是:
['首頁', '服務', '關於我們']
那是不是 map() 自己幫我們用逗號隔開?
不是。
逗號只是 Array 裡不同資料之間的分隔。
[
'首頁',
'服務',
'關於我們'
]
代表裡面有三筆資料。
如果真的希望把 Array 變成:
首頁|服務|關於我們
比較像是:
menuItems.join('|')
map() 和 join() 做的是不同事情。
可以先這樣分:
map()
→ 每一筆拿去加工
→ 得到新的 Array
join()
→ 把 Array 裡面的資料串成一個字串
所以map()和join()都只是處理Array的方法之一,還有很多種方法~~
map()?回到我們真正的問題。
原本資料:
const menuItems = [
'首頁',
'服務',
'關於我們'
]
如果:
100 → 80
可以用 map(),
那:
首頁 → <a>首頁</a>
其實也是一種加工。
所以可以寫:
menuItems.map((item) => {
return <a>{item}</a>
})
概念上就是:
'首頁'
↓
<a>首頁</a>
'服務'
↓
<a>服務</a>
'關於我們'
↓
<a>關於我們</a>
我們只是把:
數字 → 新數字
換成:
資料 → JSX
而已。
你可能會問JSX是什麼?JSX就是看起來很像是html的語法,但實際上是寫在JavaScript裡面:
例如這個function裡面像是html的都是jsx
function App() {
return (
<div>
<h1>Cruxilion</h1>
<p>Hello React</p>
</div>
)
}
這時候突然就比較能理解,為什麼 React 裡到處都是:
something.map(...)
因為前端很常做的事情,就是:
把一堆資料轉成一堆畫面。
現在 Header 可以變成:
function Header() {
const menuItems = [
'首頁',
'服務',
'關於我們'
]
return (
<header>
<nav>
{menuItems.map((item) => {
return <a>{item}</a>
})}
</nav>
</header>
)
}
export default Header
這裡又看到 Day 10 出現過的:
{}
在 JSX 裡:
{menuItems.map(...)}
可以先理解成:
JSX 走到這裡,請執行這段 JavaScript。
map() 最後產生:
<a>首頁</a>
<a>服務</a>
<a>關於我們</a>
React 再把這些元素顯示到畫面上。
寫到這裡,很可能會看到 React 跳出:
Each child in a list should have a unique "key" prop.
第一次看到其實滿莫名其妙。
明明畫面都出來了。
為什麼還要 key?
假設資料是:
const menuItems = [
{ id: 1, name: '首頁' },
{ id: 2, name: '服務' },
{ id: 3, name: '關於我們' }
]
我們可以寫:
menuItems.map((item) => {
return (
<a key={item.id}>
{item.name}
</a>
)
})
這裡:
key={item.id}
可以先把它想成每一筆資料的「身分證」。然後照這個寫法,就是在JS的function裡面使用{key:value}。
一開始:
key=1 → 首頁
key=2 → 服務
key=3 → 關於我們
後來我們把順序改成:
const menuItems = [
{ id: 1, name: '首頁' },
{ id: 3, name: '關於我們' },
{ id: 2, name: '服務' }
]
現在變成:
key=1 → 首頁
key=3 → 關於我們
key=2 → 服務
因為 key 沒變,React 可以知道:
服務沒有消失
只是移到別的位置
關於我們也不是新出現
只是換位置
很像班上換座位。
如果:
A001 王小明
A002 陳小華
A003 林小美
今天三個人全部換位置,老師還是知道誰是誰。
因為老師不是靠:
第一個座位
第二個座位
第三個座位
認人。
而是靠:
A001
A002
A003
React 的 key 大概就是這種角色。
所以可以先記:
map()
→ 把資料變成很多 JSX
key
→ 幫 React 認出每一筆資料
id 當 key?如果資料本來就有:
{
id: 2,
name: '服務'
}
那:
key={item.id}
通常很合理。
因為 id 本來就是這筆資料自己的身分。
例如:
function Header() {
const menuItems = [
{ id: 1, name: '首頁' },
{ id: 2, name: '服務' },
{ id: 3, name: '關於我們' }
]
return (
<header>
<nav>
{menuItems.map((item) => {
return (
<a key={item.id}>
{item.name}
</a>
)
})}
</nav>
</header>
)
}
照上面這串程式碼,可以得到的結果:
[
<a key={1}>首頁</a>,
<a key={2}>服務</a>,
<a key={3}>關於我們</a>
]
現在 Header 的選單資料和畫面結構終於分開了。
想新增:
案例分享
主要是新增一筆資料,而不是再複製一整段 <a>。
今天一開始的問題其實很單純。
原本我們是這樣寫選單:
<a>首頁</a>
<a>服務</a>
<a>關於我們</a>
後來把真正會改變的資料抽出來:
const menuItems = [
'首頁',
'服務',
'關於我們'
]
再用:
map()
把 Array 裡的資料一筆一筆拿出來。
menuItems.map((item) => {
return <a>{item}</a>
})
所以今天其實串起了這條路:
Array
↓
map() 一筆一筆拿資料
↓
item 代表這一輪拿到的資料
↓
return 加工後的結果
↓
得到新的 Array
在一般 JavaScript 裡,可能是:
100
↓
80
在 React 裡,則很常是:
'首頁'
↓
<a>首頁</a>
也就是:
資料
↓
JSX
↓
畫面
這也是為什麼 React 裡這麼常看到 map()。
另外今天還碰到了:
join()
它和 map() 不一樣。
map()
→ 一筆一筆加工
→ 最後得到新的 Array
join()
→ 把 Array 裡的資料接在一起
→ 最後得到一個 String
最後,當 React 要顯示一整串 map() 產生的 JSX 時,我們又碰到了:
key={item.id}
key 可以先把它理解成 React 用來辨認每一筆清單元素的「身分證」。
如果 map() 這一輪直接回傳:
<a key={item.id}>
{item.name}
</a>
最外層是 <a>,所以 key 就放在 <a> 上。
為了讓每個選單都有自己的 id,剛才我們把資料改成了:
const menuItems = [
{ id: 1, name: '首頁' },
{ id: 2, name: '服務' },
{ id: 3, name: '關於我們' }
]
這時候 Array 裡面裝的,已經不只是:
'首頁'
而是:
{
id: 1,
name: '首頁'
}
這個 {} 不是 JSX。
它是 JavaScript 的 Object。
裡面的資料是:
id → 1
name → '首頁'
也就是一組一組的:
key: value
現在只有 id 和 name 還看不太出它的必要。
但網站不可能永遠只有:
首頁
服務
關於我們
假設一個選單之後還要知道:
文字
網址
是否顯示
icon
權限
那一筆資料可能就會慢慢長成:
{
id: 1,
name: '首頁',
href: '/',
visible: true,
icon: 'home',
role: 'all'
}
這時候就會發現:
Array
→ 負責裝「很多筆選單」
Object
→ 負責描述「其中一筆選單有哪些資料」
所以我們下一個問題就很自然了:
下一篇就從 JavaScript 的 Object 開始~~
孩子睡了~晚安ZzZz 好累QQ