Before you're going to hate it, then you're going to love it.html
在 18年的 JSConf Iceland 上, Dan 神提到 Concurrent Render 涉及到 CPU 以及 IO 這兩方面。前端
Time Slicing 對應解決左側的問題, Suspense 對應解決了右側的問題。它們共同要解決的是的提高用戶體驗, 在更多的場景下均可以作到可交互
。而 Fiber 架構是上述二者的基石。react
在 16 以前的版本的渲染過程能夠想象成一次性潛水 30 米,在這期間作不了其它事情(Stack Reconciler);git
痛點歸納:github
接着拿上面的潛水例子爲例,如今變爲能夠每次潛 10 米,分 3 個 chunk 進行; chunk 和 chunk 之間經過鏈表鏈接; chunk 間插入優先級更高的任務, 先前的任務被拋棄。web
開啓 Fiber 後,獲取異步數據的方法應放到 render 後面的生命週期鉤子裏(phase 2 階段)進行, 由於 render 前面的生命週期鉤子(phase 1階段)會被執行屢次編程
注意: 並無縮短原先組件的渲染時間(甚至還加長了),但用戶卻能感受操做變流暢了。api
requestIdleCallback(): 借力此 api, 瀏覽器能在空閒的時間處理低優先級的事。數組
Suspense 意思是能暫停當前組件的渲染, 當完成某件事之後再繼續渲染。瀏覽器
import React, { lazy, Suspense } from 'react' const OtherComponent = lazy(() => import('./OtherComponent')) function MyComponent() { return ( <Suspense fallback={<div>loading...</div>}> <OtherComponent /> </Suspense> ) }
一種簡單的預加載思路, 可參考 preload
const OtherComponentPromise = import('./OtherComponent'); const OtherComponent = React.lazy(() => OtherComponentPromise);
在 React16 版本中 render() 增長了一些返回類型,到目前爲止支持的返回類型以下:
其中 render() 支持返回 Arrays 能讓咱們少寫一個父節點, 以下所示:
const renderArray = () => [ <div key="A">A</div> <div key="B">B</div> ]
render() 支持返回數組的特性相似 Fragments(16.2), 使用 Fragments 能夠不用寫 key。
將 React 子節點渲染到指定的節點上
案例:實現一個 Modal 組件,demo
另外關於 Portals 作到冒泡到父節點的兄弟節點這個現象, demo, 我想能夠這樣子實現:若是組件返回是 Portal 對象,則將該組件的父組件的上的事件 copy 到該組件上。其實並非真的冒泡到了父節點的兄弟節點上。
React 16 提供了一個新的錯誤捕獲鉤子 componentDidCatch(error, errorInfo)
, 它能將子組件生命週期裏所拋出的錯誤捕獲, 防止頁面全局崩潰。demo
componentDidCatch 並不會捕獲如下幾種錯誤
服務端渲染通常是做爲最後的優化手段, 這裏淺顯(缺少經驗)談下 React 16 在其上的優化。
在 React 16 版本中引入了 React.hydrate()
, 它的做用主要是將相關的事件注水
進 html
頁面中, 同時會比較前端生成的 html
和服務端傳到前端的 html
的文本內容的差別, 若是二者不一致將前端產生的文本內容替換服務端生成的(忽略屬性)。
在 React 16 版本中, 支持自定義屬性(推薦 data-xxx
), 於是 React 能夠少維護一份 attribute 白名單, 這也是 React 16 體積減小的一個重要因素。
Context 至關因而用組件化的方式使用 global, 使用其能夠共享認證的用戶、首選語言(國際化)等一些全局的信息, 而沒必要經過組件一層層傳遞。
如下是比較冗餘的傳遞:
<Page riderId={riderId} /> // ... which renders ... <RiderDetail riderId={riderId} /> // ... which renders ... <RiderLevel riderId={riderId} /> // ... which renders ... <Avatar riderId={riderId} />
在 Context
以前能夠傳遞 <Avatar>
自己(Component Composition 的思想), 寫法以下:
function Page(props) { const avatar = <Avatar riderId={props.riderId} /> return <RiderDetail avatar={avatar} /> } <Page riderId={riderId} /> // ... which renders ... <RiderDetail avatar={avatar} /> // ... which renders ... <RiderLevel avatar={avatar} /> // ... which renders ... { props.avatar }
接着是使用 Context
書寫的例子, 寫法以下:
const RiderContext = React.createContext(1) // 這裏爲默認值 function Page(props) { const riderId = props.riderId return ( <RiderContext.Provider value={riderId}> <RiderDetail /> </RiderContext.Provider> ) } function RiderDetail() { return <RiderLevel /> } class RiderLevel extends React.Component { static contextType = RiderContext render() { return <Avatar avatar={this.context} />; } }
在將來 17 的版本中,將移除的生命週期鉤子以下:
componentWillMount()
: 移除這個 api 基於如下兩點考慮:
遷移思路, 將之前寫在
componentWillMount
的獲取數據、時間訂閱的方法寫進componentDidMount
中;
componentWillReceiveProps(nextProps)
: 移除這個 api 基於以下考慮:
舉個例子: 好比切換 tab 時都要從新獲取當前頁面的數據, 以前一般會這麼作:
componentWillReceiveProps(nextProps) { if (nextProps.riderId !== this.props.riderId) { fetchData() } }
新的鉤子 getDerivedStateFromProps()
更加純粹, 它作的事情是將新傳進來的屬性和當前的狀態值進行對比, 若不一致則更新當前的狀態。以前 componentWillReceiveProps()
裏的獲取數據的邏輯以前提到 Concurrent render
的時候也提到了應該後置到 componentDidUpdate()
中。
getDerivedStateFromProps(nextProps, prevState) { if (nextProps.riderId !== prevState.riderId) { return { riderId: nextProps.riderId } } // 返回 null 則表示 state 不用做更新 return null }
componentWillUpdate()
: 目前將其理解爲和 componentWillMount
同樣的狀況在 React 16.3 的版本中,新加入了兩個生命週期:
getDerivedStateFromProps(nextProps, prevState)
: 更加語義化, 用來替代 componentWillMount()
和 componentWillReceiveProps(nextProps)
;
getSnapshotBeforeUpdate(prevProps, prevState)
: 能夠將該鉤子返回的結果傳入 componentDidUpdate 的第三個參數中, 從而達到 dom 數據統一。用來替代 componentWillUpdate();
具體 demo 可見 Update on Async Rendering
React.memo
是一個高階組件, 它使無狀態組件擁有有狀態組價中的 shouldComponentUpdate()
以及 PureComponent
的能力。
const MyComponent = React.memo(function MyComponent(props) { ... })
在 React 16.7 以前,React 有兩種形式的組件,有狀態組件(類)和無狀態組件(函數)。Hooks 的意義就是賦能先前的無狀態組件,讓之變爲有狀態。這樣一來更加契合了 React 所推崇的函數式編程。
接下來梳理 Hooks 中最核心的 2 個 api, useState
和 useEffect
useState 返回狀態和一個更新狀態的函數
const [count, setCount] = useState(initialState)
使用 Hooks 相比以前用 class 的寫法最直觀的感覺是更爲簡潔
function App() { const [count, setCount] = useState(0) return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) }
在每次 render 後都會執行這個鉤子。能夠將它當成是 componentDidMount
、componentDidUpdate
、componentWillUnmount
的合集。所以使用 useEffect 比以前優越的地方在於:
componentDidMount、componentDidUpdate
書寫重複的代碼;useEffect
;(在之前得寫進不一樣生命週期裏);今年的 React Conf 的一張圖, 能夠看到 React 從出來到如今勢頭呈穩健上升趨勢, 並在 2018 年這個節點上把 Jquery 拉下了王座。但能夠看見 React 將來還有一段很長的路要走。