一、老版本的生命週期以及按順序執行的流程
Initialization (初始化階段)
這些方法會在組件初始化的時候被調用,只跟實例的建立有關。react
若是用 createReactClass 進行建立,則還有 getInitialState 這些生命週期函數,但不多使用,咱們這裏不說起。算法
static defaultProps{} (getDefaultProps())
定義默認 props ,會和父組件傳下來的 props 進行合併,且以父組件中的 props 優先級更高,至關於 {...defaultProps, props} 。redux
static propTypes{} (getInitialState())
定義 props 的數據類型,能夠幫助咱們肯定其有效性,減小許多開發中沒必要要的錯誤。瀏覽器
constructor()
在加載階段前調用一次,進行 state 的初始化。性能優化
constructor(props){服務器
super(props)
}網絡
super(props) 用來調用父類的構建方法。數據結構
Mounting(加載階段)
componentWillMount()
新版中爲 UNSAFE_componentWillMount() 。架構
只在組件加載時調用,整個生命週期只調用一次,之後組件更新也不會調用,此時能夠修改 state。dom
render()
react 中最重要的生命週期函數,建立虛擬 dom,並進行 diff 算法,更新 dom 樹也在此進行。因此不該該在此改變組件的狀態或者與瀏覽器進行交互。
注意:這個函數不能缺乏,若是不建立虛擬 dom,能夠 return null 。
componentDidMount()
組件加載完成後當即調用,整個生命週期只調用一次,能夠獲取到更新後的 DOM,在此處能夠進行網絡請求等。
Updating(更新階段)
componentWillReceiveProps()
新版中爲 UNSAFE_componentWillReceiveProps() 。
在組件加載後,若是有新的 props 傳遞過來,會先調用這個函數,能夠在這裏調用 setState() 修改 state 。
componentWillReceiveProps(nextProps)
shouldComponentUpdate()
react 中一個重要的性能優化點,組件接收到新的 props 或者 state ,返回 true 表示須要更新 dom,返回 false 阻止更新。
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate()
新版中爲 UNSAFE_componentWillUpdate() 。
組件加載時不調用,只有在組件須要更新(即 shouldComponentUpdate 返回 true )時調用。
componentWillUpdate(nextProps, nextState)
注意:不能在這個方法中調用 setState() 修改 state 。
不然會從新觸發updating
render()
componentDidUpdate()
在組件更新完成後當即被調用,能夠進行網絡請求等。
componentDidUpdate(prevProps, prevState)
Unmouting(銷燬階段)
componentWillUnmount()
在組件被卸載和銷燬以前調用,能夠在這裏處理任何須要的清理工做,好比解除定時器,取消已經發起的網絡請求,清理在 componentDidMount 函數中建立的 DOM 元素。
componentWillUnmount()
Catching(捕獲階段)
componentDidCatch()
錯誤邊界捕獲,在v16.0剛推出的時候新增長的一個生命週期函數,用於捕獲在 子組件樹 中任意地方發生的JavaScript 錯誤,一個錯誤邊界不能捕獲它本身內部的錯誤。
componentDidCatch(error, info)
二、新版本中新增的生命週期以及對應的使用方法
首先,新版本中有三個生命週期被deprecate(去掉)了,他們分別是:
componentWillReceiveProps
componentWillMount
componentWillUpdate
getDerivedStateFromProps()
這個getDerivedStateFromProps是一個靜態函數,因此函數體內不能訪問this,簡單說,就是應該一個純函數,純函數的話,輸出徹底由輸入決定。
static getDerivedStateFromProps(nextProps, prevState) { //根據nextProps和prevState計算出預期的狀態改變,返回結果會被送給setState }
每當父組件引起當前組件的渲染過程時,getDerivedStateFromProps會被調用,這樣咱們有一個機會能夠根據新的props和以前的state來調整新的state,若是放在三個被deprecate生命週期函數中實現比較純,沒有反作用的話,基本上搬到getDerivedStateFromProps裏就好了;
注意:getDerivedStateFromProps不管是Mounting仍是Updating,也不管是由於什麼引發的Updating,所有都會被調用。
getSnapshotBeforeUpdate()
這函數會在render以後執行,而執行之時DOM元素尚未被更新,給了一個機會去獲取DOM信息,計算獲得一個snapshot,這個snapshot會做爲componentDidUpdate的第三個參數傳入。
getSnapshotBeforeUpdate(prevProps, prevState) { console.log('#enter getSnapshotBeforeUpdate'); return 'foo'; } componentDidUpdate(prevProps, prevState, snapshot) { console.log('#enter componentDidUpdate snapshot = ', snapshot); }
getSnapshotBeforeUpdate把snapshot返回,而後DOM改變,而後snapshot傳遞給componentDidUpdate。
總結: 用一個靜態函數getDerivedStateFromProps來取代被清除的幾個生命週期函數,就是強制開發者在render以前只作無反作用的操做,並且能作的操做侷限在根據props和state決定新的state,而已。
這是進一步施加約束,防止開發者亂來
Q&A:
一、Willmount or didmount?
以前想過爲何不在componentWillMount裏寫AJAX獲取數據的功能,以前的觀點是,componentWillMount在render以前執行,早一點執行早獲得結果。
可是,要知道,在componentWillMount裏發起AJAX,無論多快獲得結果也趕不上首次render,
並且componentWillMount在服務器端渲染也會被調用到(固然,也許這是預期的結果),
這樣的IO操做放在componentDidMount裏更合適。在Fiber啓用async render以後,更沒有理由在componentWillMount裏作AJAX,
由於componentWillMount可能會被調用屢次,誰也不會但願無謂地屢次調用AJAX吧。
其實這個問題react的開發者早就有想過,因此才運營而生了後面的新的生命週期,既然不想放在這些很差的生命週期裏面,那都不如直接讓他們乾脆沒辦法作。
二、何時用forceUpdate?
forceUpdate就是從新render。有些變量不在state上,可是你又想達到這個變量更新的時候,刷新render;
或者state裏的某個變量層次太深,更新的時候沒有自動觸發render。
這些時候均可以手動調用forceUpdate自動觸發render。因此建議使用immutable來操做state,redux等flux架構來管理state。
三、父組件的狀態變化怎麼影響子組件的生命週期?
shouldComponentUpdate函數接受nextProps和nextState做爲參數。
你能夠根據props裏面設的值或者state裏面設的值來判斷需不須要更新。例如判斷this.state.count是否等於nextState.count。
四、爲何在react componentWillUpdate中調用setstate會形成循環調用?
若是在shouldComponentUpdate和componentWillUpdate中調用了setState,
此時this._pendingStateQueue != null,則performUpdateIfNecessary方法就會調用updateComponent方法進行組件更新。
可是updateComponent方法又會調用shouldComponentUpdate和componentWillUpdate,
所以形成循環調用,使得瀏覽器內存佔滿後崩潰。
五、函數式組件有沒有生命週期?爲何?
由於函數式組件沒有繼承React.Component,因爲生命週期函數是React.Component類的方法實現的
因此沒繼承這個類,天然就無法使用生命週期函數了
六、PureComponent和Component有什麼區別?
PureComponent優點: 不須要開發者本身實現shouldComponentUpdate,就能夠進行簡單的判斷來提高性能。 PureComponent缺點: React.PureComponent 經過props和state的淺對比來實現 shouldComponentUpate()。
在PureComponent中,若是包含比較複雜的數據結構,可能會因深層的數據不一致而產生錯誤的否認判斷,致使界面得不到更新。
PureComponent 在使用 shouldComponentUpdate的處理是由 PureComponent 自身來處理,而不是由用戶來控制,
因此咱們在 PureComponent 中若是複寫今生命週期回調函數,React 會提示咱們錯誤的。告訴咱們不容許重寫該方法。
七、爲何PureComponent比較複雜的數據結構,可能會因深層的數據不一致而產生錯誤的否認判斷?
JavaScript 中的對象通常是可變的(Mutable),由於使用了引用賦值,新的對象簡單的引用了原始對象,
改變新的對象將影響到原始對象。
如
foo={a: 1}; bar=foo; bar.a=2
你會發現此時 foo.a 也被改爲了 2。
爲了解決這個問題,通常的作法是使用 shallowCopy(淺拷貝)或 deepCopy(深拷貝)來避免被修改,但這樣作形成了 CPU 和內存的浪費。
緣由在於js使用的是引用賦值,新的對象簡單引用了原始對象,改變新對象雖然影響了原始對象,
但對象的地址仍是同樣,使用===比較的方式相等。
而在PureComponent中,會被斷定prop相等而不觸發render()。
避免此類問題最簡單的方式是,避免使用值可能會突變的屬性或狀態,而是使用副原本返回新的變量
八、react中的setstate是同步的仍是異步的?Rax呢?
由React控制的事件處理程序,以及生命週期函數調用setState不會同步更新state 。
React控制以外的事件中調用setState是同步更新的。好比原生js綁定的事件,setTimeout/setInterval等。
大部分開發中用到的都是React封裝的事件,好比onChange、onClick、onTouchMove等,這些事件處理程序中的setState都是異步處理的。
Rax的setState是同步的
九、React是怎樣控制異步和同步的?
在 React 的 setState 函數實現中,會根據一個變量 isBatchingUpdates
判斷是直接更新 this.state 仍是放到隊列中延時更新,
而 isBatchingUpdates 默認是 false,表示 setState 會同步更新 this.state;
可是,有一個函數 batchedUpdates,該函數會把 isBatchingUpdates 修改成 true,
而當 React 在調用事件處理函數以前就會先調用這個 batchedUpdates將isBatchingUpdates修改成true,
這樣由 React 控制的事件處理過程 setState 不會同步更新 this.state。
十、setState的一些用法?
(1)多個setState調用會合並處理:
在某些處理程序中調用了兩次setState,可是render只執行了一次。
由於React會將多個this.setState產生的修改放在一個隊列裏進行批延時處理。
(2)參數爲函數的setState用法
假如setState更新state後我但願作一些事情,
而setState多是異步的,那我怎麼知道它何時執行完成。
因此setState提供了函數式用法
接收兩個函數參數,第一個函數調用更新state,第二個函數是更新完以後的回調。
case:
handleClick() {
this.setState({
count: this.state.count + 1
})
}
以上操做存在潛在的陷阱,不該該依靠它們的值來計算下一個狀態。
handleClick() {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
最終的結果只加了1
由於調用this.setState時,並無當即更改this.state,
因此this.setState只是在反覆設置同一個值而已,上面的代碼等同於這樣
handleClick() {
const count = this.state.count
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
}
count至關於一個快照,因此無論重複多少次,結果都是加1。
第一個函數接收先前的狀態做爲第一個參數,將這次更新被應用時的props作爲第二個參數。
increment(state, props) {
return {
count: state.count + 1
}
}
handleClick() {
this.setState(this.increment)
this.setState(this.increment)
this.setState(this.increment)
}
對於屢次調用函數式setState的狀況,React會保證調用每次increment時,state都已經合併了以前的狀態修改結果。
也就是說,第一次調用this.setState(increment),
傳給increment的state參數的count是10,第二調用是11,第三次調用是12,最終handleClick執行完成後的結果就是this.state.count變成了13。
值得注意的是:在increment函數被調用時,this.state並無被改變,
依然要等到render函數被從新執行時(或者shouldComponentUpdate函數返回false以後)才被改變,
由於render只執行一次。