# 十年回首:React 的過去、現在與未來發展 - Zet
{%hackmd LeyMdnM3Q4ipfr57bkqpyA %}
#### [📚 議程介紹](https://webconf.tw/agenda/day1-5-f)
###### ▼▼▼ 開始筆記 ▼▼▼
[十年回首:React 的過去、現在與未來發展簡報-Slides](https://slides.com/tz5514/react-webconf2024)
講者 200X 年初次接觸 React
## React 誕生的時空背景
2011-2012
* 對效能更高的需求
* 狀態管理,資料連動畫面更新
* 程式碼重用、管理的需求
* Facebook (Meta)面對FB與IG產品需求
2013
* Facebook 在 JSConf 發表並開源
* 設計理念:
* component-based
* 單向資料流
## Componet base
### ProductItem.jsx
```javascript
var ProductItem = React.createClass({
render: function() {
return (
<div>
<div>{this.props.name}</div>
<div>價格: {this.props.price}</div>
</div>
);
}
});
```
### ProductList.jsx
```javascript
var ProductList = React.createClass({
getInitialState: function() {
return {
products: []
};
},
componentDidMount: function() {
ProductAPI.fetch().then(function(data) {
this.setState({
products: data
});
}.bind(this));
},
render: function() {
return (
<div>
<div>商品列表</div>
{this.state.products.map(product => (
<ProductItem
name={product.name}
price={product.price}
/>
))}
</div>
);
}
});
```
## React 的單向資料流與畫面渲染策略
### 單向資料流:資料驅動畫面
* 畫面結果是原始資料透過模板與渲染邏輯所產生的延伸結果
* 資料更新時,畫面才會產生對應的更新,以資料去驅動畫面
不被允許逆向改資料也就是說,透過畫面逆向改資料是不行的情況,例如 input。(雙向資料流可以透過 input 改變資料)
### 限縮變因的價值
在單向資料流的設計模式下,資料變動只會來自開發者手動觸發資料更新,畫面也只會由**原始資料**與**模板邏輯**這兩種變因構成。
好處:
* 可維護性提高
* 可讀性提升
* 減少資料出錯的風險
* 效能優化
### 策略一:資料更新後,人工判斷並手動修改所有連動的 DOM element
```javascript=
const counterValues = [0, 0, 0];
function getNumbersSum(numbers) {
return numbers.reduce((x, y) => x + y);
}
function incrementCounterAndUpdateDOM(index) {
counterValues[index] += 1;
// 資料更新後,需要具體知道這次資料的更新會影響到的 DOM 範圍,並且手動一一去更新:
// 修改某個 counter 的 value 資料後,
// 該 counter 對應的 <li> 裡面的 <span> 的文字內容會需要更新
document
.querySelectorAll('#counter-list > li > span')
.item(index)
.textContent = counterValues[index];
// 修改某個 counter value 資料後,也會需要重新計算並更新 counter sum 的文字內容
document
.querySelector('#counter-sum > span')
.textContent = getNumbersSum(counterValues);
}
function initialRender() {
// 只有初始化 render 時才會遍歷整個 counterValues 來印出每個 counter item
document.body.innerHTML = `
<div id="counters-wrapper">
<ul id="counter-list">
${counterValues.map((counterValue, index) => `
<li>counter ${index}: <span>${counterValue}</span></li>
`).join('')}
</ul>
<div id="counter-sum">
counters sum: <span>${getNumbersSum(counterValues)}</span>
</div>
</div>
<button id="increment-btn">increment counter 0 & 2</button>
`;
// increment button 事件綁定
const incrementButton = document.getElementById('increment-btn');
incrementButton.addEventListener('click', () => {
// 範例行為:increment counter 0 & counter 2
incrementCounterAndUpdateDOM(0);
incrementCounterAndUpdateDOM(2);
});
}
initialRender();
```
優點:只要開發者 DOM 操作的夠精確,可以減少多餘DOM操作的效能浪費
缺點:完全依照人為判斷,難以長期維護。
### 策略二:資料更新後,一律將畫面的 DOM element 清除,再以最新的原始資料全部重繪
```javascript=
const counterValues = [0, 0, 0];
function getNumbersSum(numbers) {
return numbers.reduce((x, y) => x + y);
}
function handleIncrementButtonClick() {
// 範例行為:increment counter 0 & counter 2
counterValues[0] += 1;
counterValues[2] += 1;
// 在更新資料後,不需要判斷這次資料更新具體會影響到的 DOM elements 有哪些,
// 一率直接呼叫 renderScreen() 來將整個畫面全部的 DOM elements 都清除後再全部重繪
renderScreen();
}
function renderScreen() {
// 每次要繪製新的畫面之前,都先把整個瀏覽器畫面全部清空
document.body.innerHTML = '';
// 依據目前的最新資料,重新繪製一次整個畫面的所有 DOM elements
document.body.innerHTML = `
<div id="counters-wrapper">
<ul id="counter-list">
${counterValues.map((counterValue, index) => `
<li>counter ${index}: <span>${counterValue}</span></li>
`).join('')}
</ul>
<div id="counter-sum">
counters sum: <span>${getNumbersSum(counterValues)}</span>
</div>
</div>
<button id="increment-btn">
increment counter 0 & 2
</button>
`;
// 重新綁定 increment button 事件
document
.getElementById('increment-btn')
.addEventListener('click', handleIncrementButtonClick);
}
renderScreen();
```
優點:只需關注模板及資料更新,不需要手動維護連動的畫面操作,維護更直覺簡單。
缺點:隨應用變龐大複雜,一律重繪會因大量不必要的 DOM 操作而產生效能問題,影響UI體驗。
## 前端框架的處理策略
* 不管是什麼策略都有缺點
* 前端框架可以透過特殊的架構設計,幫助解決資料連動畫面更新的需求,保留渲染策略的優點,同時解決缺點。
* 例如 Vue MVVM(策略一) ,藉著 proxy 監聽來源資料,自動更新資料綁定的畫面。
React 採用策略二:當資料更新後,一律將整個畫面的 DOM element,全部清除,再以最新的原始資料來全部重繪
## DOM 與 Virtual DOM
解釋什麼是 DOM, Virtual DOM 定義。
Virtual DOM 是一種自創的資料結構
用虛擬的畫面結構來模擬真實的 DOM element
React 讓你先產 Virtual DOM 再產生實體 DOM
## 建立 React Elememt
```javascript
import React from 'react';
const buttonReactElement = React.createElement(
'button', // 元素類型
{ id: 'foo-btn' }, // 屬性
'I am a button' // 子元素
);
```
由於create
jsx 來自於 react.createElement 的語法糖。
react 在run time的時候才知道畫面長怎樣
比較沒辦法在build time 的時候處理優化,換句話說語法分析也比較困難。
vue 用 v-if, v-on 語法相對受限,使用template,但也因此更容易調整及優化。
## React 的一律重繪渲染策略
若一律全部清掉全部重繪,會很浪費效能。
=> 改成一律重繪虛擬的畫面結構資料。
把前一次印出來的跟後一次印出的DOM比較找到差異。盡量減少操作 DOM element。
效能瓶頸是在建立react element 的過程
*講完基本策略接下來會講重大的 feature 發展。*
## Class Component
```javascript=
```javascript
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
}
componentDidMount() {
ProductAPI.fetch().then(function(data) {
this.setState({
products: data
});
}.bind(this));
}
render() {
return (
<div>
<div>商品列表</div>
{this.state.products.map(product => (
<ProductItem
name={product.name}
price={product.price}
/>
))}
</div>
);
}
}
```
有一段時間是主流的寫法
## Create React App(2016)
由於需要 babel 轉譯 剛開始需要複雜的設定,進入門檻較高
因此創建了CRA腳手架
## React Fiber(2017)
### 為啥需要架構重寫?
* 前端對效能需求增加
- 前端對於「多任務」處理能力的需求增加
- 任務的兩大分類
- CPU 密集處理
- I/O(data fetch / 使用者互動)
- 任務之間的依賴關係
- 任務之間的優先級關係
### 在 React Fiber 之前
- Render phase
- 由父 component 層層往內部的子 component 呼叫來產生 UI 樹狀結構,形成一個**遞迴**的 stack
- 這個 stack 的產生過程是同步且不可中斷的,因此當 render 某一塊樹狀結構過深或過於複雜的畫面時,就有可能佔用瀏覽器的 main thread 過久,導致其他瀏覽器工作產生延遲,影響使用者體驗
- Commit phase
- 這部分會呼叫瀏覽器 API 來更新實際的 DOM,因此本來就必定是同步且不可中斷的
以前是遍例整棵樹狀結構,這個過程不可中斷。
### 在使用 fiber 之後
透過link list 來處理,因為只需要記得當下的節點位置
## concurrent mode
當時再2018 要用就要全部用,在社群有些爭議
## streaming ssr
必須載入所有的js 才可以
## cocurren feature
從current mode 改成cureent feature
##
useTrisition
##
useDeferredValue
## server component
## meta framework
- Next.js
- Remix
## use
use讚讚
## 聊天區