本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。
在進行專案前筆記一些React的基礎觀念,在專案進行中穿插這些觀念感覺太破碎,先整理起來。
React 用 component 的方式讓頁面能夠拆分成許多部件,方便去思考各個部件要:
延續學習Vue的時候對Component的理解,在學習React的時候也是從MVC三點入手,接下來就按順序討論,先談 View。
React切版的基礎單元是 React element,這東西來自:
React.createElement(
type,
[props],
[...children]
)
簡單範例:
React.createElement("div", null, "Hello world!");
上面的程式編譯成html會變成:
<div>Hello world!</div>
這個function的詳細介紹就跳過了,因為平常根本不會用到,只是他是JSX的基礎,知道有這個function後再來介紹JSX比較有頭緒,大概了解一下就好。
React 建構UI的方式就是將一個個element堆疊起來,但一直寫React.createElement實在太繁瑣了,像是要產生
<div>
<p>Hello world!</p>
</div>
用React.createElement的話:
React.createElement(
"div",
null,
React.createElement(
"p",
null,
"Hello world!"));
每多一層就要多掛一個React.createElement在children的位子上,
雖然能用變數(例: const Hello = React.createElement(...))取代,但還是繁瑣,這也是JSX登場的時候了。
先來個簡單的JSX範例:
const element = <div>Hello, world</div>
嗯? 這是 html?但是有 expression,所以是 javascript?
初次看到 JSX 的時候用之前學樣板語言的方式去理解總覺得哪裡怪怪的,後來在閱讀文件後覺得要理解 JSX ,首先要認識的就是, JSX 跟一般樣板語言最不同的部分在於編譯後首先產生的是 Object ,不是 html。
JSX 經由 Babel 編譯後首先會轉成 React.createElement 的 Object ,而既然是 Object ,就能像上面的範例一樣,參照給 expression,也能用陣列存起一串 JSX ,像是:
const list = [
<li>1</li>,
<li>2</li>,
<li>3</li>
];
或者用條件式判斷要回傳不同的JSX
if(show === true){
return <h1>YES</h1>
}
else{
return <h1>NO</h1>
}
而巢狀的 JSX ,也是由 children 的部分先轉成 object,傳給 createElement 後產出上層的 object
JSX:
const element = (
<div>
<h1 name="title">Hello world!</h1>
</div>
)
編譯後
const element = React.createElement(
"div",
null,
React.createElement(
"h1",
{name: "title"},
"Hello world!"));
所以之前提到的繁瑣工作就由Babel代勞了,因為 JSX 近似於 html 格式,也不需要再煩惱層狀構造怎麼進行轉換的問題。
關於 JSX 怎麼轉換成 createElement 的可以到Babel的線上編譯器進行嘗試。
理解 JSX 能夠作為 object 在 javascript 中運用後,來看看怎麼在 JSX 當中運用 javascript。
const element = <img src={user.avatarUrl}></img>;
大括號裡可以帶入任何 javascript expression,像是 arr[0] , {id:"1",name:"user1"} (會有兩層大括號),arrow function 也可以。
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
格式像這樣
{ [條件判斷式] &&
[判斷為true時回傳的JSX]
}
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
格式像這樣
{ [條件判斷式] ? [true時回傳的JSX]:[false時回傳的JSX]}
function NumberList() {
const numbers = [1, 2, 3, 4, 5];
return (
<ul>
{numbers.map((number) => (
<li key={number.toString()}>{number}</li>
))}
</ul>
);
}
利用javascript 的 Array.map() 函式將陣列轉換成JSX陣列,當成其他樣板語言裡的 for loop 理解就行。
要注意的重點在 key ,必須指定獨特的 key 給每個 sibling,不然 React 在比對 virtual dom 的時候會無法分辨 element 間的差別。關於 virtual dom 容後再談,先了解 key必須是獨特值,一般會用資料的 id,再不然也要用陣列index,不過index會有其他問題,這也是之後再談。
不過key的獨特性只要保持在同node下的siblings之間就好,不同node底下map清單的話key相同也無妨。
談完 JSX 的使用後再回頭來了解下JSX最後是怎麼跟html掛鉤的。
看看上一篇用CRA建立的專案裡,index.js當中
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
ReactDOM.render可說是React框架的主輸出口,在所有component用JSX產出element一個個往上傳遞,最後集大成的element就會由ReactDOM.render渲染到指定的node底下,像是這邊的 document.getElementById("root")。
也可以呼叫多個ReactDOM.render在不同的root上渲染element,不過當ReactDOM.render第一次被呼叫的時候會清空目標底下所有node,再掛上自己渲染的node,所以如果多個render對一個root的話只會剩下最後一個。