# 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`