# JS React(09/10) 五倍紅寶石(第八屆共筆)(2021/8/24)
## 講師簡介及建議
奶綠茶,目前任職於 Positive Grid
先找一份工作再說,工作兩三年後再看對前後端哪個有興趣
很少有人能做到全端,建議挑一個
解決問題、溝通表達、會寫文件的能力很重要!
### cdn.js
open source 套件包,有很多好用的套件
可以不用輸入終端機指令,直接安裝套件
全球有部署很多主機,會自動判斷你的位置在哪,再幫你找離你比較近的主機位置
[cdnjs](https://cdnjs.com/)
### CSS
寫兩個 id,有的瀏覽器後者寫的會蓋掉前者
## React 簡介
有大佬在背後撐著(Facebook)
只負責 MVC 的 View 部份,因此不算框架,彈性高
React 只提供大框架,裡面還是要自己寫JS
Vue 則是有他自己的語法,類似 Rails 黑魔法的概念,要再額外花心力去記語法
- 優點
只要專注在 data-binding 就好也就是專注在資料上
剩下的東西 React 會幫你 render 出來
完全由 JS 操作 UI,使得它可以跟後端分離,達到即時互動、自動更新的效果
- 原生 DOM:
元素實際存在於網頁中,網頁檢查原始碼可看的到
早期 google 搜尋引擎只會爬靜態資料,但現在改了,也會支援 Virtual DOM 的 SEO
yahoo、bin瀏覽器目前還是不支援
- Virtual DOM
所有元素都是用 React 動態長出的,是虛擬元素,網頁檢查原始碼看不到
React 可以做到前後端分離,前後端只要互相知道規格就可以用 api 溝通
但用傳統的 JS 很難 co-work
### JSX
在 JS 裡面寫類似 HTML 的語法,長得很像 HTML 但卻是 JS,可以在 view 產生 HTML
### 實作
- babel.js:
因為 JSX 語法瀏覽器不認識,所以交由 babel 幫你即時編譯 JSX 內容成 JS,讓瀏覽器看得懂,
但 cdn 的 babel 效能很低,不要在網站上使用,可以用終端機指令安裝在本地端
參考:
[搞懂為何設定 REACT、JSX、ES2015、BABEL、WEBPACK 的學習筆記](https://blog.turn.tw/?p=3532)
[[ES6-重點紀錄] 開發環境建置(上) - Babel 編譯工具](https://ithelp.ithome.com.tw/articles/10197028)
01_helloworld
```js
// ReactDOM 裡面有 render 方法,第一個參數是 JSX,告訴 HTML 我要render h1 這個 JSX 動態元素,記得後面要加逗號!第二個參數是我要長在哪裡
// 一個 ReactDOM.render 只能有一個根節點,如果放超過一個會出錯:Adjacent JSX elements must be wrapped in an enclosing tag
// 解決方法:用 div 把他包起來就只有一個根節點
ReactDOM.render(
<div>
<h1>123</h1>
<h2>123</h2>
</div>,
document.getElementById('app')
);
```
02_component
```js
// 早期寫法,現在不要這樣寫了
class MyComponent extends React.Component {
render() {
return (
<div className="my-component">
這是ReactComponent組件
</div>
);
}
}
// 現代寫法
// FunctionalComponent 就是 JS 的 function
// component 名稱一定要用大寫開頭,才能透過 JSX 去編譯,同時也可以跟 HTML 標籤做區分
// return JSX 元素
// class 要寫成 className,跟原本的 CSS 做區分
// JSX 內用{}包起來代表的是 JS 的運算
// 沒包的就是 JSX 語法
// return 的()是為了防止 JS 自動幫你加 ; 導致 return undefined
function FunctionalComponent() {
return (
<div className="functional-component">
<br/>
{
new Date().toDateString()
}
</div>
)
}
// 所有的元件(component)跟 JSX 元素都要有結束標示 "/"
ReactDOM.render(
<div>
<MyComponent />
<FunctionalComponent />
</div>
,
document.getElementById("app")
)
```
03_props
```js
// 初學者用 component 接收參數的唯一方法就是透過 props 取得,不要打破這個原則
// 進階版的先不用學
// props 是 object
// const {img, name, children} = props 是用解構賦值方式取出 key (img,name,children)
// children 是特有的 key,內建的關鍵字
// 所有要透過 JS 運算的東西都是用 {} 包起來 ex:{img} {name} {children}
// name: 只是字串,因為是包在<div></div>裡面,跟一般 HTML 標籤用法一樣
function FunctionalCard(props){
const {
img, name, children
} = props;
return (
<div className="card">
<img className="img" src={img}/>
<div className="name">name:{name}</div>
<div className="quote">quote:{children}</div>
</div>
);
}
// 客製化的 component
// 把 參數(img="http://fakeimg.pl/300x100/ecf0f1/" name="milkmidi")丟到 component 裡
// 如果參數裡面有 JSX(這邊的<h1>標籤),回傳時會把他丟到props 裡的 children,用 children 來接收
// 如果在這邊直接用內建關鍵字 children 當屬性名稱傳入 prop ,React 預設還是會傳入(這邊的<h1>標籤)
// props 傳的參數一律都是字串,如果要傳其他型態的值的話要用{}包起來,React 會自動幫你判斷他的原始資料型別
ReactDOM.render(
// TODO2
<div className="app">
<FunctionalCard
img="http://fakeimg.pl/300x100/ecf0f1/"
count={1}
bool={true}
name="milkmidi" />
<FunctionalCard
img="http://fakeimg.pl/240x80/ecf0f1/"
name="奶綠茶">
<h1>我是子元素</h1>
</FunctionalCard>
</div>
//
,
document.getElementById('app')
);
```
04_setState
```js
// 這邊的 useState 是 hooks 用法,專案一定會用到
// 原本是宣告私有變數0 回傳給 array
// [count, setCount] 是 array 的解構賦值
// const [count, setCount] 的第二個參數專門用來更改第一個參數,呼叫 setCount 時,count 就加1
// setCount 被觸發時瀏覽器會重新 render,並且使用 React 提供得 useState 瀏覽器才會知道要更新頁面
// onClick = addeventlisten(click)
function Counter(props){
const {
// 加預設值防呆,怕使用者輸入沒有輸入值會變成undefined,造成結果是NAN
initCount = 0
} = props;
const [count, setCount] = React.useState(initCount);
const atClick = () => {
setCount(count + 1);
}
return (
<div className="counter">
<h1>Counter</h1>
<div className="count">{count}</div>
<button className="my-btn" onClick={atClick}>+1</button>
</div>
);
}
// 加{}讓字串變數字
ReactDOM.render(
<div>
<Counter initCount = {0}/>
<Counter initCount = {1}/>
</div>
,
document.getElementById('app')
);
```
05_Conditional-Rendering
```js
// 透過 true false(選擇性切換) 來切換要 render 登入成功 還是 Please sign up.
// 三元運算子在 React 很常用
// React 不是用 display none/block 切換 CSS,而是直接刪掉再重新render,所以效能會差一點點
function App(){
const [isLoggedIn, setLoggedIn] = React.useState(false);
const atClick = () => {
setLoggedIn(!isLoggedIn);
}
return (
<div className="app">
<button onClick={atClick} >{isLoggedIn ? "登出" : "登入"} </button>
{isLoggedIn ? <UserGreeting name="milkmidi" /> : <GuestGreeting /> }
{isLoggedIn && <UserGreeting name="milkmidi" />}
</div>
)
}
```
06_style-class-bind
```js
// 用某個變數控制 class 名稱
// 第二個參數用來更改第一個參數,所以每次點擊按鈕都會觸發 setGreen 改變 isGreen 狀態
// style={{}} 的第一個{}是 JSX 第二個{}是物件
// <div className={boxClassName} /> 因為沒有要在div裡面塞東西所以直接截斷,看起來比較簡短
function App(){
const [isGreen, setGreen] = React.useState(false);
const atClick = () => {
setGreen(!isGreen);
}
var boxClassName = 'box';
if (isGreen) {
boxClassName += ' style-green';
}
return (
<div className="app">
<button className="my-btn" onClick={atClick}>isGreen</button>
<div className={boxClassName} />
<div style={{
width: 200,
height: 200,
backgroundColor: isGreen ? 'green' : 'red',
display: isGreen ? 'block': 'none'
}} />
</div>
)
}
```
07_useEffect
app.js
```js
// 用 show 控制 <Clock /> 的出現及消失
// 用來觸發 Clock.js 的 useEffect
function App(){
const [show, setShow] = React.useState(false);
const atClick = () => {
setShow(!show);
}
}
```
Clock.js
```js
// useEffect 知道component的建立與死亡,建立跟死亡都只會發生一次
// 常用在api上,被建立就打api到後端
// React 的 生命周期方法:componentDidMount、componentWillUnmount,是固定用法,可參考官網
function Clock() {
React.useEffect(() => {
// 建立時發生,執行componentDidMount ex: setinterval
console.log('componentDidMount')
return () => {
// 死亡時發生,執行componentWillUnmount ex: clearinterval
console.log('componentWillUnmount');
}
}, []);
}
```
08_list-rendering
```js
// 點擊後新增資料進去
// 使用原生 JS return JSX 元素出來時,用迴圈產生的 vitual dom 會遺失 key,所以要手動幫他寫一個 key 值
// Key 幫助 React 分辨哪些項目被改變、增加或刪除。在 array 裡面的每個 element 都應該要有一個 key,如此才能給予每個 element 一個固定的身份
// https://zh-hant.reactjs.org/docs/lists-and-keys.html
// className 加 data 屬性就可以在瀏覽器看到
// 不然一般寫的 JSX 在瀏覽器看不到
// 傳值跟傳址(傳記憶體位址)
// === 型別跟記憶體位置都一樣才會是true
// react 不會 render 型別跟記憶體位置都一樣的變數
// 所以要使用 concat 讓原本的 array 的值一樣但產生新的記憶體位置
// 這樣才能 render 出來
// 當初 react 設計理念就是這樣,因為背後是用 === 去做判斷
// 不希望你去動到裡面的東西
function List(){
const [list, setList] = React.useState(['學會 JS', '學會 React', '年薪百萬']);
const atAddClick = () => {
var newList = list.concat(new Date().toString());
setList(newList);
}
return (
<div>
<button onClick={atAddClick}>Add</button>
<ol className="list">
{
list.map(function (text) {
return <li key={text}>{text}</li>;
})
}
</ol>
</div>
)
}
```
09_list-rendering-advance
```js
// 打 api,接api
// 用 useEffect 的時候可以打
// 是實際上會用到的開發技巧!
function List(){
const [list, setList] = React.useState([]);
React.useEffect(() => {
fetch("./categories.json")
.then((res) => res.json())
.then((categories) => {
console.log(categories)
setList(categories)
})
}, [])
return (
<div>
<div className="category-wrap">
{
list.map((category) => {
return (
<CategoryItem
key={category.id}
name={category.name}
image={category.image} />
)
})
}
</div>
</div>
)
}
```
### 補充
- 數位邏輯
10 >> 1 = 5
- 把網頁包進應用程式
VScode & Spotify 本身是網頁,用打包工具把網頁塞進去變應用程式
可以在說明那邊開開發者工具檢視原始碼檢查
- 浮點數
瀏覽器物件距離的最小單位是1px
有的瀏覽器(ie)用浮點數畫面會變得怪怪的,可能會超自然抖動
- setinterval
使用 setinterval 會造成 memory leak
搭配 useState 使用時要用 clearinterval 清掉才不會記憶體越疊越高造成 stack overflow
- vscode extensions
https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer
https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets
https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree
https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag
settings.json 設定:
"emmet.includeLanguages": {
"javascript": "javascriptreact"
}
- JS Vue推薦書
Kuro 老師的[0 陷阱!0 誤解!8 天重新認識 JavaScript!(iT邦幫忙鐵人賽系列書)](https://www.tenlong.com.tw/products/9789864344130)[重新認識Vue.js:008天](https://www.books.com.tw/products/0010883365)
- 課後練習
1. 自己做出一個todo-list
2. 想想看專案哪一部分可以用 React 寫,為什麼要用?
---
###### tags: `JavaScript` `React`