# 1.21 React ###### tags: `React` ## 零、目錄&待辦事項 ## 壹、ES6複習 ### 一、let、const #### 1. 函式表達式用const來宣告較佳 ```javascript= const foo = function(){...} ``` #### 2. 區域作用域 vs 函式作用域 * 區域作用域:if/else、for迴圈的{}裡面;ES6開始在這裡面就是local * for迴圈()中的let變數仍然是在local * for迴圈中的let變數會重新綁定 * 函式作用域:function的{}裡面;ES5前在這裡面才是local ```javascript= for (var i = 0; i < 10; i++){ setTimeout(()=> console.log('i:', i), 1000) } // Output: i:10; 10組 // var i 是全域變數,所以最後會變成10 // 在執行的時候,JS是去記i的記憶體位置,也就是每次都是記得i,而不是那個值,最後顯現時才把它全部印出來 for(let i = 0; i < 10; i++){ setTimeout(()=> console.log('i:', i), 1000) } // Output: i:0, i:1, ..., i:9 // let i 是區域變數,每次都會綁定一次 // 所以最後會是10個不同的i // 這個相當於下面的寫法 let k; for(k = 0; k < 10; k++){ let i = k; setTimeout(()=>console.log('i:', i), 1000) } ``` ### 二、箭頭函式 * 箭頭函式複習 ```javascript= const foo1 = function(x){ return x + 1 } const foo2 = (x) => x + 1 // 省略function、{}、return ``` ```javascript= const foo2 = x => x + 1 // foo2(10); // output: 11 const foo3 = x => { x + 1 } // foo3(10); // output: undefined // 只有單一參數時,可以再省略() // 只有單一回傳值(一行)時,可省略花括號跟return,才會有自動腦補return // 如果沒有省略花括號,則沒有腦補return ``` * JSX語法搭配箭頭函式 JSX語法有很多列,此時可以用()作為分行的排版符號,放在()內的會被視為同一行,所以可以省略{}、return ```javascript= const HelloWorld = (props) => ( <div> <h1>{props.text}</h1> </div> ) // <div><h1>{props.text}</h1></div> 是一行 ``` * 箭頭函式和一般函式的差別 ![](https://i.imgur.com/vhKN5ht.png) * 箭頭函式沒有arguments物件,這裡Eddy沒有解釋什麼,只說了這是個奇怪的東西 ```javascript= const bar = function(x,y){ console.log(arguments)} bar(1,2); // 有一串奇怪的東西 bar(1,2,5); //這樣也跑得出來喔 ``` ### 三、傳入預設值 * 在沒有給參數或是給的式undefined時,才會使用預設值 * null的話會是null,不會用預設值 * JS的空值是undefined,不是null ### 四、解構賦值 ### 五、展開與其餘運算符 * 陣列跟物件都適用展開運算符(ES8) ```javascript= const newArray = [...arr, b] // 相當於把b push到原陣列內 ``` * 其餘運算符 ```javascript= function(...a){} // 不特定個數的參數 const[a, ...b] = [1,2,3] // a=1、b=2,3 ``` ### 六、類別 ### 七、模組系統 * Export & Import * [可以看這篇的介紹](https://wcc723.github.io/development/2020/03/25/import-export/) * 模組系統不是每個瀏覽器都可以使用,還是會用babel轉譯,caniuse有解釋各瀏覽器運作 #### 1. Export * Export 可以將函式、物件甚至是純值匯出,大部分運用上都是匯出物件(函式)為主,畢竟純值作為模組意義並不大。 * 分為Export default 預設導出、Export 具名導出 * Export default 預設導出:一支程式只能有一個預設導出,通常導出函式、類別(不要導出變數、常數) * Export 具名導出 ## 貳、React技術入門 ### 一、React是什麼 react是JS的函式庫,用來建立使用者的操作介面 ### 二、五大特性 虛擬DOM、JSX語法、元件化、單向資料流、宣告式程式設計 #### 1. 虛擬DOM - React自創的DOM元素與結構語法 * DOM:Doucment Object Model,文件物件模型 * DOM指的就是HTML的程式介面,提供了樹狀的結構化呈現,每一個節點就是一個DOM節點 。 * 虛擬DOM是react自創管理的dom結構,經與真實DOM差異比較並調和一致(reconciliation)後,再做渲染(render)。 * 產生頁面就叫做渲染,白話為轉譯和呈現 * 為何要用虛擬DOM? * 現在的網頁太複雜,開發者用真實DOM太難寫了。像是FB有各種功能,不好寫。 * React處理速度其實不會比直接DOM處理更快,只是他協助開發者建立較好維護的迎用程式。 #### 2.JSX語法 - 搭配虛擬DOM的語法,類似於HTML * JSX:JS語法的擴充,JS=JavaScript,X=XML * JSX中{}類似於樣板字串,可以用於代入變數、求值運算 * 這裡的JSX不是一般的JSX,是react自創的JSX * 需要透過babel編譯才能執行 * babel是JS的編譯器。JS是直譯語言,為何還需要編譯? * JS功能越來越多,且支援的裝置越來越多樣化,相容性要求越來越高,因此需要轉譯。 ```javascript= const element = <div>Hello World</div> // 這其實瀏覽器看不懂,要再經過babel編譯 // 是React.createElement的簡寫,經babel編譯後相當於下面的寫法 var element = React.createElement( "div", null, "Hello World" ) ``` #### 3.元件化-開發採用元件分離與組合的方式 依照功能性拆解成多個小元件,開發多個小元件再組合 #### 4.單向資料流-從父母元件(擁有者)流到子女元件(被擁有者)(從外到內) 利用單向流達到元件化,目前只有單向(其餘框架大多雙向) #### 5.宣告式程式設計-如何更動與呈現交由React 指令式:明確告知每一步怎麼做 宣告式:告知電腦目標就好,詳細步驟交給他(例如SQL語法) ### 三、react16新核心-React Fiber 上課是以16版為主。 * ??? * 太難啦 * React Fiber(纖呈):讓工作程序更小,達到並行的異步渲染(Async Rendering) ## 參、React環境設定與安裝 [相關細節還有設定檔可以看這裡](https://github.com/eyesofkids/mfee11-react/tree/main/%E6%95%99%E6%9D%90/0121/reactjs%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/CRA-eslint-prettier) ### Step.1 安裝yarn ``` npm install --global yarn ``` yarn指令: 1. yarn start:啟動伺服器 2. yarn build:專案發佈到線上時用的(大專只用一點點) 3. yarn test:模擬網頁上的使用者操作行為(大專不會用到) 4. yarn eject:跳出設定檔,永久性的動作,無法回復(大專不會用到)。設定檔藏在node_module裡面有個react-scripts,裡面有config ### Step.2 建立React專案 [協助建立react專案的官方文件](https://create-react-app.dev/docs/getting-started/) ``` 目標資料夾內cmd npx create-react-app 專案名稱 ``` ### Step.3 安裝ESlint & Prettier模組以及各自的VScode套件 * 安裝好後還要載入Eddy的設定檔,npm模組跟VScode都有各自的設定檔 * babel在安裝react專案時就會自動安裝 * 都安裝完後,檢查右下角是不是有打勾的ESLint,有的話就成功了。如果是紅色的就按下去啟動。 ### Step.4 啟動伺服器 yarn start ### Step.5 由.env更改埠號 在react專案資料夾內建立一個.env檔,裡面寫PORT=3006 ## 肆、檔案結構介紹 ### 1. index.html(這隻先不會動): * 也就是local:3000,入口的html檔案。 * 內容只有一個root元素,也沒有script。所有的DOM元素都會渲染到這個節點。 * 除非要引入一些特別的CSS才會動到這個檔案,通常不會動。 ### 2. index.js(這隻先不會動): * 對應到index.html。 * ReactDOM.render:有二個參數,一個element跟一個dom,把app這個元件渲染到root,也就是所謂的把虛擬dom轉為真實dom。 * React.StrictMode:React.StrictMode > react會自動檢查開發的元件是不是有問題。可寫可不寫。 ### 3. App.js: * 是個元件 * 元件分為函式型及類別型 ```javascript= // App.js // 第一行在以前的版本要寫,但後來更新版本會自動引入這行 // 因為有JSX語法,JSX式react.createlement的簡寫,所以要有import import React from 'react' function App(){ return <h1>Hello</h1> } expor default App ``` ## 伍、函式型元件 * react元件二大重點:屬性、狀態 開發者透過元件的狀態,告訴react如何改變網頁上的內容 * 鉤子是什麼? * react的hook(鉤子)其實就是react內建的函式、API,綁定react一些功能 * useState 是鉤子的一種,會產生二個值分別為得到狀態、設定狀態 * 經由useState這個鉤子,創造total這個狀態,並給予初始值為0。以及創造setTotal這個設定狀態的方法。 * [元件撰寫原則](https://github.com/eyesofkids/mfee11-react/issues/2) * 資料類型、建構函式大駝峰,方法小駝峰 * js檔名只看得懂$、_這二個符號(但不要用) ### 一、計數器 ```javascript= // 程式碼/0121/1.計數器/App.js // 這行是因為React用JSX語法,React.createElement的簡寫,所以需要import // 導入useState這個API // 以前的函式型元件不能使用狀態,hook後才可以 // 部分導入,所以是{} import{ useState } from 'react' function App(){ // 宣告一個新的state變數,名稱為"total" // 這段語法是解構賦值的語法 // useState會產生陣列,內有二個值,初始值為0,total得到狀態、setTotal設定狀態 const[total, setTotal] = useState(0) return ( <> <h1 onClick={()=>{ setTotal(total+1) }}> {total} </h1> </> ) } export default App // 這是假的、人造的h1,讓react模仿h1,做一個onclick的屬性,要做setTotal這件事情。 // 這是間接的處理,原本JS的const不能更改 // react要改變狀態就只能用設定狀態的方法 // return只能有一個東西,所以要把h1跟btn放在空的<>內 ``` ### 二、計數器-按鈕 ```javascript= // 程式碼/0121/2.計數器-用按鈕/App.js import{ useState } from 'react' function App(){ const[total, setTotal] = useState(0) return ( <> <h1>{total}</h1> // 多了button後會跳eslint error,JSX must be wrapped in an enclosing tag (必須具有一個父元素) <button onClick={()=>{ setTotal(total+1) }}>+1</button> <button onClick={()=>{ setTotal(total-1) }}>-1</button> </> // h1、btn是三棵樹種在一片土地上。只能return一棵樹。所以要用fragment把h1、btn*2包裹起來變成一棵樹。 // 回傳的JSX語句只能有一個跟元素。必要時須使用<>...</>包住 ) } export default App ``` ### 三、拆解App、CountFunc元件 ```javascript= // App.js // 程式碼是寫在CountFunc,在App內使用 import CountFunc from './components/CountFunc' function App(){ return( <> <CountFunc /> </> ) } // components/CountFunc.js import {useState} from 'react' function CountFunc(){ const[total, setTotal] = useState(0) return( <> <h1>{total}</h1> <button onClick = {()=>{ setTotal(total+1) }}>+1</button> <button onClick{()=>{ setTotal(total-1) }}>-1</button> </> ) } export default CountFunc ``` ### 四、類別型元件語法 ```javascript= // 類別型元件較麻煩,一定要有constructor跟render,還要使用react內建的setState import React from 'react' class CountClass extends React.Component { constructor() { super() this.state = { total: 0, } } render() { return ( <> {/* 和函式型元件不同,這裡是使用物件的寫法 */} <h1 // JS內是onclick="...",所以變成onClick={...} onClick={() => { // setState是來自於react.component // 裡面要放的是物件 this.setState({ total: this.state.total + 1 }) }} > {this.state.total} </h1> </> ) } } export default CountClass ``` ## 伍、類別(不是這堂課的重點) [類別的講義在這裡](https://github.com/eyesofkids/mfee11-react/blob/main/%E6%95%99%E6%9D%90/0121/ES6%E7%AF%87-%E9%A1%9E%E5%88%A5class.pdf) 但很多都跳過RRR ### 一、this指的是誰 在類別型物件中,this指的是由new方法所建立出來的物件實體 ## 陸、元件的狀態(state)與屬性(props) [看看這裡還不錯](https://hackmd.io/@Heidi-Liu/note-fe302-component-jsx) * React的核心思想就是元件化思想,頁面會被切分成一些獨立的、可複用的元件。 * 元件從概念上看就是一個函式,可以接受一個引數作為輸入值,這個引數就是props,所以可以把props理解為從外部傳入元件內部的資料。由於React是單向資料流,所以props基本上也就是從服父級元件向子元件傳遞的資料。 * 一個元件的顯示形態可以由資料狀態和外部引數所決定,外部引數也就是props,而資料狀態就是state。 * state不同於props的一點是,state是可以被改變的。不過,不可以直接通過this.state=的方式來修改,而需要通過this.setState()方法來修改state。 * state是元件內部的私有資料,用於儲存因時間經過或是使用者操作而變動的資料,可變; * props是外部傳入的資料引數,不可變(只有父母元件能變動); * 沒有state的叫做無狀態元件,有state的叫做有狀態元件; * 多用props,少用state。也就是多寫無狀態元件。 * setState方法是非同步的 ## 柒、SelectBox.js ### 一、Q:import {useState} from 'react'、import React from 'react' 部分引用跟全部引用 ```javascript= // CountClass.js import React from 'react' // 也可以寫成import React, {components} from 'react' class CountClass extends React.Components { ... } //改成class CountClass extends Components{...} // CountFuncs.js import {useState} from 'react' // 一樣的~import React, {useState} from 'react' function CountFunc(){ const [total, setTotal] = useState(0) } // 也可以改成 import React from 'react' function CountFunc(){ const[total, setTotal] = React.useState(0) } ``` ### 二、SelectBox * 解釋setState是非同步,會較晚執行這件事情 * 解決方法是塞到一個變數,其他需要用的就帶這個變數 ```javascript= // App.js import SelectBox from './components/SelectBox' function App(){ return( <> <SelectBox /> </> ) export default SelectBox ``` ```javascript= // components/SelectBox.js import React, {useState} from 'react' function SelectBox(){ const [text, setText] = useState(0) // // 這二行const這樣會有問題 // const handleChange = (e) =>{ // setText(e.target.value) //後執行 // console.log(text) //先執行 // } //解決上面setState是非同步的問題 const handleChange = (e) =>{ //先計算最後的值,塞到一個變數,再用他來做事情 const newText = e.target.value //給react呈現畫面用 setText(newText) //後執行 //做別的事情,像是跟資料庫連動 console.log(newText) //先執行 } return( <> <select onChange={this.handleChange} value={this.state.value}> <option value="JavaScript" key={1}> JavaScript </option> <option value="Angular2" key={2}> Angular2 </option> <option value = "React" key={3}> React </option> </select> <h1>{text}</h1> </> ) } ```