本文整理了 React 16.x 出現(xiàn)的耳目一新的概念與 api 以及應(yīng)用場(chǎng)景。
更多 React 系列文章可以訂閱blog
16.0 Fiber
在 16 之前的版本的渲染過程可以想象成一次性潛水 30 米,在這期間做不了其它事情(Stack Reconciler);
痛點(diǎn)概括:
- 一次性渲染到底
- 中途遇到優(yōu)先級(jí)更高的事件無(wú)法調(diào)整相應(yīng)的順序
在 16 版本上, React 帶來(lái)了 Fiber 的架構(gòu), 接著拿上面的潛水例子為例,現(xiàn)在變?yōu)榭梢悦看螡?10 米,分 3 個(gè) chunk 進(jìn)行; chunk 和 chunk 之間通過鏈表連接; chunk 間插入優(yōu)先級(jí)更高的任務(wù), 先前的任務(wù)被拋棄。
開啟 Fiber 后,獲取異步數(shù)據(jù)的方法應(yīng)放到 render 后面的生命周期鉤子里(phase 2 階段)進(jìn)行, 因?yàn)?render 前面的生命周期鉤子(phase 1階段)會(huì)被執(zhí)行多次
注意: 并沒有縮短原先組件的渲染時(shí)間(甚至還加長(zhǎng)了),但用戶卻能感覺操作變流暢了。
render()
在 React16 版本中 render() 增加了一些返回類型,到目前為止支持的返回類型如下:
- React elements.
- Arrays and fragments.
- Portals.
- String and numbers.
- Booleans or null.
其中 render() 支持返回 Arrays 能讓我們少寫一個(gè)父節(jié)點(diǎn), 如下所示:
const renderArray = () => [
<div>A</div>
<div>B</div>
]
個(gè)人認(rèn)為 render() 支持返回?cái)?shù)組完全可以取代 Fragments
Portals(傳送門)
將 react 子節(jié)點(diǎn)渲染到指定的節(jié)點(diǎn)上
案例:實(shí)現(xiàn)一個(gè) Modal 組件,demo
另外關(guān)于 Portals 做到冒泡到父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)這個(gè)現(xiàn)象, demo, 我想可以這樣子實(shí)現(xiàn):如果組件返回是 Portal 對(duì)象,則將該組件的父組件的上的事件 copy 到該組件上。其實(shí)并不是真的冒泡到了父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)上。
Error Boundaries
React 16 提供了一個(gè)新的錯(cuò)誤捕獲鉤子 componentDidCatch(error, errorInfo), 它能將子組件生命周期里所拋出的錯(cuò)誤捕獲, 防止頁(yè)面全局崩潰。demo
componentDidCatch 并不會(huì)捕獲以下幾種錯(cuò)誤
- 事件機(jī)制拋出的錯(cuò)誤(事件里的錯(cuò)誤并不會(huì)影響渲染)
- Error Boundaries 自身拋出的錯(cuò)誤
- 異步產(chǎn)生的錯(cuò)誤
- 服務(wù)端渲染
服務(wù)端渲染
服務(wù)端渲染一般是作為最后的優(yōu)化手段, 這里淺顯(缺乏經(jīng)驗(yàn))談下 React 16 在其上的優(yōu)化。
在 React 16 版本中引入了 React.hydrate(), 它的作用主要是將相關(guān)的事件注水進(jìn) html 頁(yè)面中, 同時(shí)會(huì)比較前端生成的 html 和服務(wù)端傳到前端的 html 的文本內(nèi)容的差異, 如果兩者不一致將前端產(chǎn)生的文本內(nèi)容替換服務(wù)端生成的(忽略屬性)。
支持自定義屬性
在 React 16 版本中, 支持自定義屬性(推薦 data-xxx), 因而 React 可以少維護(hù)一份 attribute 白名單, 這也是 React 16 體積減少的一個(gè)重要因素。
life cycle
在 React 16.3 的版本中,新加入了兩個(gè)生命周期:
getDerivedStateFromProps(nextProps, prevState): 更加語(yǔ)義化, 用來(lái)替代 componentWillMount、componentWillReceiveProps(nextProps);getSnapshotBeforeUpdate(prevProps, prevState): 可以將結(jié)果傳入 componentDidUpdate 里, 從而達(dá)到 dom 數(shù)據(jù)統(tǒng)一。用來(lái)替代 componentWillUpdate()(缺點(diǎn)是 React 開啟異步渲染后,componentWillUpdate() 與 componentDidUpdate() 間獲取的 dom 會(huì)不統(tǒng)一;
16.7 Hooks
在 React 16.7 之前,React 有兩種形式的組件,有狀態(tài)組件(類)和無(wú)狀態(tài)組件(函數(shù))。Hooks 的意義就是賦能先前的無(wú)狀態(tài)組件,讓之變?yōu)橛袪顟B(tài)。這樣一來(lái)更加契合了 React 所推崇的函數(shù)式編程。
接下來(lái)梳理 Hooks 中最核心的 2 個(gè) api, useState 和 useEffect
useState
useState 返回狀態(tài)和一個(gè)更新狀態(tài)的函數(shù)
const [count, setCount] = useState(initialState)
使用 Hooks 相比之前用 class 的寫法最直觀的感受是更為簡(jiǎn)潔
function App() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
useEffect(fn)
在每次 render 后都會(huì)執(zhí)行這個(gè)鉤子??梢詫⑺?dāng)成是 componentDidMount、componentDidUpdate、componentWillUnmount 的合集。因此使用 useEffect 比之前優(yōu)越的地方在于:
- 可以避免在
componentDidMount、componentDidUpdate書寫重復(fù)的代碼; - 可以將關(guān)聯(lián)邏輯寫進(jìn)一個(gè)
useEffect;(在以前得寫進(jìn)不同生命周期里);
在上述提到的生命周期鉤子之外,其它的鉤子是否在 hooks 也有對(duì)應(yīng)的方案或者舍棄了其它生命周期鉤子, 后續(xù)進(jìn)行觀望。