[TOC] # 2024/3/17 2-6 ~ 2-7 簡報: https://hackmd.io/@pinkymini/S1tYH5bTa#/ 導讀人: 粉粉 筆記工: Vera ## 2-6 #### 單向資料流 - 資料傳遞單向不可逆向 ![image](https://hackmd.io/_uploads/BJbJ2-ERa.png) - 資料驅動畫面:不會在原始資料更新以外的情況下更新畫面 - 資料與畫面分離管理:更好實踐資料驅動畫面 #### 單向資料流常見應用 - 資料傳遞方向:資料從父層向下傳遞 - 更新資料方向:需透過事件去從父層更動資料,不能從子層更動 #### 資料 -> DOM 實際渲染的策略: - 資料更新後,人工判斷並手動修改對應 DOM element - 可以只更新需要更新的部分來節省效能 - 需要人工判斷並維持單向資料流,難度高 - 資料更新後,整個 DOM 全部清除,以最新的原始資料重繪 - 直覺 - 效能問題:大量操作不需要被操作的 DOM #### 前端框架中的應用 - 結合 Virtual DOM - 前端框架都是選擇單向資料流 - React 策略二 - Vue 策略一 - 雙向資料流之說 - Vue: V Model,實際上是雙向綁定「語法糖」 - 選擇原因 - 限縮變因:可維護性、可讀性,減少資料意外出錯、可進行有效的效能優化 #### 雙向綁定 - 資料同步到畫面,畫面也可能同步到資料 - 唯一例外(畫面到資料):input 本身是自帶狀態,執行由瀏覽器維護、儲存。input 觸動 `onChange` 事件,瀏覽器可以直接蓋回資料,不需要人工處理。相對而言,單向資料流:e.g. React 需要使用 `setState` 去更動資料。 ## 2-7 #### component - 開發者自定義的畫面元件藍圖,可以重複使用,內涵特定畫面意義與邏輯 - 抽象化:定義 component 時,會將邏輯與實際用途明確地定義出來 #### component 定義&呼叫 - 透過 JS 函式定義,把 props 傳入,回傳 React element - compononent function 第一個字母須大寫:JSX 會根據命名來判斷 DOM element 或 Component function - 呼叫: ```javascript= // 透過建立 React comoponent 形式被呼叫 const reactElement = <CustomButtom /> // 轉譯成以下 const reactElement = React.createElement(CustomButtom) ``` - component vs. React element - component 是 React element 的藍圖,可以比喻成食譜 - React element: 建構畫面的最小單位,是實際產生的結果,可以比喻成菜餚 #### props - 客製 component 的屬性,被當作參數傳遞的 props 會被打包成一個物件,讓 component 依照不同情境產出符合需求的 React element - 使用: - 透過 props 取得自定義 props 物件:e.g. `props.price` - 解構 - 特殊 props: `children` - props 是**唯讀**而不可修改 - 維護單向資料流的可靠性:開發環境下的 React 會把 props 物件先以`Object.freeze(props)` 做處理,避免不小心改動到。如果指向到同一個記憶體位置,即使值不同,`Object.freeze()` 也不會起作用 #### 父 component 與子 component - component 裡面呼叫另一個 component 來組裝畫面 #### component 的 render 和 re-render - render: 首次呼叫 component function - re-render: 狀態資料更新,再次呼叫 component function - render 從上到下,由外至內。當父層 component render 即使子層沒有變更,底下子層也會 render ## Q & A #### Q: 當第一個H2Component有狀態的更新時,會發生什麼事? ![image](https://hackmd.io/_uploads/BJymPzVAp.png) A: 呼叫第一個 H2Component 的 component function,依序呼叫裡面的 PComponent 進行 render。不會影響到第二個 H2Component,因為只有第一個 H2Component 的狀態更新。 #### Q: 試著講出一個內含子component的父component在資料更新後,會發生哪些事情? A: 父 component 更新,會觸發子 component 的 re-render #### Q: 滾動時畫面為什麼那麼卡? ![image](https://hackmd.io/_uploads/BkEWqzV0a.png) A: 父層 position 一直被更改,因此子層會不斷 re-render。 解法: 將不能更動的子層 components 包成 slowComponent。在新舊比對中,因為原本的版本因為每次創建都是新的 object,記憶體位址也不相同,所以會需要不斷改動,但以下方解法在比對中是相同的,就可以避免多餘的 re-render。 ![image](https://hackmd.io/_uploads/Byx_9zV0a.png) #### Q: 雙向資料流在 React 上的實作 A: React 本身沒有雙向資料流,也做不到。該情境會使用 state 管理資料,將 state 設定在 input 上,觸動事件去觸發 setState 做更改,i.e. 單向資料流的設計需要開發者自己去處理這段。 #### Q: P.141 筆者思維分享與高內聚低耦合之間的 trade-off 設計? A: (component 設計心法,Zet 的經驗,是沒有特定解答的。如果要認真回答可以寫一本書。) 設計不能只是單純處理眼前情境,需要思考抽象意義分層,e.g. 兩個長得很像但實際不一樣的功能,就不能設計在一起,A、B props 都不重複 => 拆解分層、抽出共同部分。可以拆很多層,不同層都有它特定的意義,避免拆很多卻不能定義清楚它的意義。從意義去思考,而不是單純讓它動起來。 1. 假設 A、B 都有商品列表,商品列表就可以抽出來 2. 假設只有 UI 重複就只把 UI 抽出來,不同部分用 props 傳入。 -> Q: 這是讓元件變成 presentation component 和 container Component 嗎? A: 這只是部分概念。開發者也可以每一層都有資料邏輯,不過每一層都有它的意義範圍。 -> Q: 考慮 MVC 方式去拆分component 嗎? A: 前端不太用 MVC,component 不是用 MVC,component 主要是增加獨立性、降低耦合性,有他自己的邏輯、樣式模板。 ## 補充 #### 補充 1 ![image](https://hackmd.io/_uploads/SkSoAM4C6.png) P.115 React element 類型非 component 實例。 component 實例才擁有自己的狀態 #### 補充 2 [Discord 相關問題參考](https://discord.com/channels/1174010592282034216/1216737172162609283/1216737172162609283) - 為什麼使用 `React.createElement()`? - 什麼時機點會執行 component? 1. `<App />` 與 `App()` 的差異 1.1 是否會被立刻執行?是否會被當成 component 實例? ```javascript! // 不會 const a = <App />; console.log(a); // 一般 function call 方式,會立刻執行,但不會被當作一個 component,不會去建立 component 實例 const b = App(); console.log(b); ``` 1.2 React 以是否透過 `React.createElement()` 呼叫 來判斷是否是 component,而不是透過 function name 是否開頭為大小寫。透過 function call 的方式不會產生 component 實例(可使用 React dev tool 去證實) ```javascript export default function App() { return ( <div className="App"> App <Foo a={10} /> {Foo()} // 不會被看作是 component </div> ); } ``` 2. 延遲執行:執行完 `React.createElement()`,但還 `<Foo/>` 沒有實際被 call 到 - 好處:可以在內容沒有被更動時,不去 re-render。 - 到 line 14 還沒執行 `<Foo/>`,component return 後會去 reconciler 觀察是否轉換成 DOM element,之後才會交給 renderer 去處理 DOM element。 ![image](https://hackmd.io/_uploads/BJCZVQ4Ca.png) - React element 是不同歷史時刻的樣子。fiber node 才是最新狀態,需要時才會同步到 DOM element - 可參考 P.134 範例。React element 被 return 後才會執行 P.136 的流程。 - 如果物件不變,會跳過 `<Foo/>`,直接沿用上一次的結果。 - props 不固定的情況:可以使用 component 類型的 React element,讓延遲執行下的比對,記憶體位置是一樣的,就會會去沿用上一次的內容。 3. Zet 提醒用簡單的例子去實踐、理解,這個步驟很重要的習慣!!會幫助理解與內化。 # 2024/3/31 2-8 ~ 2-9 簡報: https://hackmd.io/@pinkymini/S1tYH5bTa#/ 導讀人: Nono Lin 筆記工: 粉粉 ## 2-8 #### state