owned this note
owned this note
Published
Linked with GitHub
# React 思維進化讀書會 第二組 - React 的畫面管理機制 2-1~2-3
日期:2024/2/21
導讀人: Sam Ho
# Quick Review Ch1
### What is "React"?

### JavaScript Foundation for Learning React
- *ES6 features
- *Closure
- *[Primitive type](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) & Object Type
- *Array methods
- *ES Module & import / export
- Promise & async / await
- Functional Programming
### React Development Environment
- **Requirement:** Node.js(for npm & Webpack)
- Official building tool: Create React App (CRA)
- React's frameworks
- Next.js by Vercel
- Remix by Shopify
# Ch2 React 的畫面管理機制
## 2-1 DOM 與 Virtual DOM
### DOM (Document Object Modal)
- **它本身並不是 JavaScript 語言的一部分 ([MDN](https://developer.mozilla.org/zh-TW/docs/Web/API/Document_Object_Model))**
- 樹狀資料結構

- **DOM 與瀏覽器的渲染引擎有著緊密的連動,當我們對 DOM 進行任何操作時,渲染引擎將自動執行一系列的更新過程來重新繪製畫面**
- **以最小範圍的 DOM 操作來完成所需的畫面變動**
### Virtual DOM
#### Virtual DOM 是試作品,DOM 是成品
- Virtual DOM(以下簡稱為 vDOM) 並非是從實際 DOM 複製而來
- 正確流程為: vDOM 定義期望的畫面結構,再根據 vDOM 來操作實際的 DOM。使實際 DOM 與 vDOM 結構保持一致
- 先根據畫面的需求來建立試作品,然後再以試作品做為樣本依據,來產生或修改相應的實際成品
Virtual DOM
```javascript
// vDOM 定義結構示意
const divVirtualDomElement = {
type: 'div',
props: {
class: 'foo-div',
},
children: 'Just a div element'
}
```
│
│ 將 vDOM element 經由程式處理,轉換成對應結構的實際 DOM element
│
▼
```javascript
function renderDomElement(virtualDomElement) {
// 依據 vDOM element 的資料產生出對應的實際 DOM element
// ...
}
```
│
│
│
▼
實際 DOM
```jsx
<html>
<head></head>
<body>
<div class="foo-div">Just a div element</div>
</body>
</html>
```
#### 畫面更新的策略:在新舊畫面的採排之間尋找差異之處
- 將實際 DOM 的操作範圍最小化,並現縮在這些真正需要變更的地方,來盡可能的減少因多餘的 DOM 操作而要成的效能浪費,以達到效能優化的目的
## 2-2 建構畫面的最小單位:React element
### 什麼是 React element
- **vDOM 畫面結構裡的每個元素被稱為「React element」,作為描述並組成畫面的最小單位**
- **component 應該是資料狀態以及畫面更新機制的最小切分單位,而描述並組成畫面的最小單位是 React element**
```jsx
import React from 'react'
const divReactElement = {
'div', // 元素類型
{ class: 'foo-div' }, // 屬性
'Just a div element' // 子元素
}
```
│
│ 產生後的 React element
│
▼
```javascript
// divReactElement
{
type: 'div',
props: { class: 'foo-div', children: 'Just a div element' },
key: null,
ref: null,
$$typeof: Symbol('react.element')
}
```
│
│ 經過 React 進行轉換處理
│
▼
```jsx
<html>
<head></head>
<body>
<div class="foo-div">Just a div element</div>
</body>
</html>
```
### React element 的子元素
- 一個 React element 的子元素可以是另一個 React element
- 如果子元素不只一個的話,則可以繼續在呼叫 `createElement` 方法時的第四、第五…第 N 個參數以此類推往下填
```javascript
const reactElement = React.createElement(
'div'
{ id: 'wrapper', className: 'foo'},
React.createElement('ui', className: 'list-item',
React.createElement('li', className: 'list-item', 'item 1'),
React.createElement('li', className: 'list-item', 'item 2'),
React.createElement('li', className: 'list-item', 'item 3')
),
React.createElement(
'button'
{ id: 'btn1'},
'I'm a button'
)
);
```
```json
div
│
│
▼
│—————————│
│ │
▼ ▼
ul button
│
│
│———————│———————│
│ │ │
li li li
```
```jsx
<html>
<head></head>
<body>
<div id="root-container">
<ul id="list-01">
<li class="list-item">item 1</li>
<li class="list-item">item 2</li>
<li class="list-item">item 3</li>
</ul>
</div>
</body>
</html>
```
### React element 在建立後是不可被修改的
- React element 一旦被建立後就是不可被事後修改的
- vDOM 概念的虛擬畫面結構在 React 中的實作就是 React element,它是在描述某個時間點版本的畫面結構
### React element 與 DOM element 的屬性對應和差異
1. DOM property & attribute (包含 event handler) 使用 camel case 命名
- `onclick`→ `onClick`、`tabindex` → `tabIndex`
- `aria-*` 和 `data-*` attribute 為例外,需要保持全部小寫
2. 涉及 JS 內建保留字彙改名避免意外情況發生
- `class → className`
- `<label>` 會有的 `for` 屬性 → `htmlFor`
3. inline styles 的`style`屬性的內容格式不同
- style 是以「物件」格式是指定的
- CSS property 名稱改以 camel case 來表示
```jsx
<h1 style={{ fontSize: '14px', fontWeight: 'normal' }}>Heading1</h1>
```
## 2-3 Render React element
### React DOM 與 root
[Example](https://codesandbox.io/p/sandbox/react-2-3-example-nhcz6q?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clsuafuio0006356hpn0dsgsv%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clsuafuio0002356hx243in2x%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clsuafuio0003356h2w6buvfo%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clsuafuio0005356h7mo5cffa%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clsuafuio0002356hx243in2x%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clsuafuin0001356hmtb6ral0%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252Findex.js%2522%257D%255D%252C%2522id%2522%253A%2522clsuafuio0002356hx243in2x%2522%252C%2522activeTabId%2522%253A%2522clsuafuin0001356hmtb6ral0%2522%257D%252C%2522clsuafuio0005356h7mo5cffa%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clsuafuio0004356hnwgkgyfg%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A0%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522clsuafuio0005356h7mo5cffa%2522%252C%2522activeTabId%2522%253A%2522clsuafuio0004356hnwgkgyfg%2522%257D%252C%2522clsuafuio0003356h2w6buvfo%2522%253A%257B%2522tabs%2522%253A%255B%255D%252C%2522id%2522%253A%2522clsuafuio0003356h2w6buvfo%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)
> Q: 如果 root 容器元素裡面原本有非 React 產生的 DOM 內容的話會發生什麼事情?
> A:
> root 容器已被 React 接管,無論加多少元素多會被覆蓋。
> 在轉換 React element 來產生實際的 DOM element時,都會直接無視 root 容器內原本已經存在的 DOM element,只會確保將 root 容器內的 DOM 內容同步化成語 React element 的結構對應一致
> Q: root 只能有一個嗎?
> A:
> 可以的,React 支援一個前端應用程式有多個 root 的存在
> Q: 為什麼不直接以 `document.body` 來當作 root 的容器元素
> A:
> React 官方不建議這麼做,因為可能有其他第三方的套件會針對 `document.body` 進行操作,從而導致 React 對於其內容 DOM element 的控制與管理不穩定
> 建議還是在 `document.body` 裡建立容器比較妥當
### React 只會去操作那些真正需要被更新的 DOM element
- React 會自動進行新舊 React element 的樹狀結構比較並尋找差異之處,並只操作這些差異之處所對應的實際 DOM element
- **「避免不必要時的 React element 產生動作」** 或是 **「在部分畫面沒有更新需求時重用舊有的 React element」**
### 瀏覽器環境以外的 React 畫面繪製
- Reconciler(定義結構)
- 負責定義以及管理畫面結構描述
- 在瀏覽器中的實際處理:
- 定義並產生新的 React element 來描述預期的 DOM 結構
- 在畫面有更新需求時將新舊 React element 比較差異之處,並交給 renderer 進行處理
- Render(渲染定義結構)
- 負責將畫面結構的描述會製成時計畫面成品
- 在瀏覽器中的實際處理:
- 用於瀏覽器環境的 renderer 就是 `react-dom`
- 將 React element 透過 `react-dom` 產生的 root,在瀏覽器環境中會製成實際的 DOM
- 在畫面有更新需求時,將 reconciler 已經比較出來的新舊 React element 差異之處,同步化到實際的 DOM 更新與操作
- Scheduler(核心觀念了解之後,在去了解 Fiber 的底層架構設計)
### 這樣的階段拆分可以帶來什麼效益
- renderer 其實是可以被任意替換的。只要有支援其他目標環境的 renderer 配合,React 其實也可以用於管理並產生瀏覽器 DOM 以外的 UI 或畫面。([Taro -> 支援鴻蒙 HarmonyOS(華為) -> 中國國家信創工程](https://docs.taro.zone/docs/harmony/))
- Learn once, write anywhere (學一次,用萬處)