JSX 的本質
JSX 代碼本身并不是 HTML,也不是 Javascript,在渲染頁面前,需先通過解析工具(如babel)解析之后才能在瀏覽器中運行。
babel官網可查看 JSX 解析后的效果
更早之前,Babel 會把 JSX 轉譯成一個 React.createElement()
函數調用,功能與現在的 jsxs 函數類似
React.createElement() 的語法
相當于 vue 的 h 函數
- 第1個參數:標簽名(字符串),或組件(變量)
- 第2個參數:屬性為key 的對象
- 之后的參數是子元素,多個子元素,也可以用數組包裹放在第三個元素的位置
- 返回 vnode
例如
const element = (<h1 className="greeting">Hello, world!</h1>
);
會被Babel 轉譯為
const element = React.createElement('h1',{className: 'greeting'},'Hello, world!'
);
React.createElement() 會創建對象
// 注意:這是簡化過的結構
const element = {type: 'h1',props: {className: 'greeting',children: 'Hello, world!'}
};
這些對象被稱為 “React 元素”。它們描述了你希望在屏幕上看到的內容。React 通過讀取這些對象,然后使用它們來構建 DOM 并保持隨時更新。
SyntheticEvent 合成事件機制
react 中的事件,不是原生事件(vue 中是原生事件),而是SyntheticEvent 合成事件。
為什么要用合成事件機制 ?
- 更好的兼容性和跨平臺(自定義封裝的合成事件對象,更方便添加兼容性和跨平臺的相關代碼)
- 統一掛載到 document 或 id 為root 的根節點 (從 react 17 開始是 root ),可以減少內存消耗,避免頻繁解綁
- 方便事件的統一管理(如事務機制)
為什么從 react 17 開始,要改為綁定到 root?
因為這樣更利于多個 React 版本并存,例如微前端。
組件渲染過程
- 通過 props ,state 初始化變量
- 通過 render() 函數生成虛擬節點 vnode
- 通過 patch(elem, vnode) 函數(不一定叫 patch,具體可能是其他名字,功能相同)渲染頁面
組件更新過程
- 通過 setState(newState) 修改響應式變量,生成 dirtyComponents (可能有子組件)
- 通過 render() 函數生成虛擬節點 vnode
- 通過 patch(elem, vnode) 函數(不一定叫 patch,具體可能是其他名字,功能相同)渲染頁面
更新的 patch 分為兩個階段
- reconciliation 階段-執行 diff 算法,純 JS 計算
- commit 階段-將 diff 結果渲染 DOM
之所以分為了兩個階段,是為了方便 fiber 優化性能
fiber 優化性能的原理
fiber 是 React 的內部運行機制
- 將 reconciliation 階段拆分成多個任務(commit 階段無法拆分)
- 當 DOM 需要渲染時暫停 reconciliation 中的任務,等無需進行 DOM 渲染時繼續執行 reconciliation 中的任務
通過 window.requestIdleCallback 可獲知 DOM 是否需要渲染,但 window.requestIdleCallback 有的瀏覽器不支持,所以在不支持window.requestIdleCallback 的瀏覽器上,無法使用 fiber 優化性能
哪些場景需要 fiber 優化性能 ?
因 JS 是單線程,且和 DOM 渲染共用一個線程,當組件足夠復雜,組件更新時,計算和渲染的壓力都很大,同時再有 DOM 操作需求(動畫,鼠標拖拽等),就很容易出現頁面卡頓(DOM 渲染等待 JS 計算)