本篇博文基於 React 16.5.2javascript
做爲一個後端開發,15年開始關注大前端發展趨勢,於17年去線下聽了場前端開發會議,那個時候Vue2.0剛出沒多久,就被那快速構建頁面給吸引了。最先重返前端仍是大半年前,新項目用vue寫了幾個功能頁面,發現如今寫前端是真挺舒服,尤爲是對於後端人員來講(排除掉CSS),快速入門並上手不是什麼問題。html
至於爲何最終選擇了react而非vue?是由於當時對react和vue及RN和weex作了番調研對比,介於weex的不給力,及後期react和vue學習成本差很少,但react的社區更爲活躍,外加發起者背景,就毅然選擇了react。(我我的是通一精百的支持者,因此對於react的理念(learn once,write anywhere),是很同意的。而weex的理念(write once,run anywhere)雖然很吸引人,但時下我的以爲技術並未達到此程度,配置的複雜度及大量的輪子需造,難以知足大型項目的要求。包括目前JD推出的Taro,我的目前持觀望態度,等到react這塊應用到項目以後,再碼一波Taro實際調研一番)。前端
接觸React的時候已是React 16.3,不由感慨前端發展至今,越有後端的趨勢。先後花了3個多月的時間過了一遍webpack4,npm,react,在公司內部作了幾場培訓,發現了其中的一些不協調,但隨着版本的迭代,這些不協調也依次在被更正。(看React17的更新內容,本來一些摸棱兩可的方法、屬性元素命名均會獲得改善:)vue
後續會以此文爲契機,開一個專欄,記錄、分享公司現有電商項目逐步遷移至react技術棧。java
本篇博文會對React16.5.2中的經常使用生命週期函數作一些翻譯、講解說明及發表一些我的的理解和見解,若有錯誤、歧義之處,還望你們不吝嗇指出,互相交流:)react
開篇借用官方提供的生命週期函數圖:(雖然是React 16.4的,但一樣適用於16.5版本) webpack
出處: projects.wojtekmaj.pl/react-lifec…react組件生命週期函數說明官方地址:reactjs.org/docs/react-…web
getDerivedStateFromProps()方法的16.3版本與以後的16.4和16.5有所調整:npm
在16.3版本上,該方法僅在props變更時纔會被觸發。canvas
在16.3以後的版本上,該方法調整爲props和state的變更都會觸發該方法。
從上圖中,咱們能夠看到,React的生命週期函數主要集中在三個階段:掛載階段(Mounting),更新階段(Updating)和卸載階段(Unmounting)。
其中:
下面咱們就對以上生命週期函數作進一步說明:
class TestContainer extends PureComponent {
state = {
content:"1" //先執行
}
constructor(props){
super(props);
this.state={
content:"2" //後執行,最終 this.state.content 爲 2
}
}
。。。
}
複製代碼
這裏的 side-effects 方法,翻譯出來是 "反作用" 的意思,我的以爲不妥,有點生澀,可能翻譯成"附加做用"更爲穩當。
它指的是,render()方法應該只完成它的主要功能(如初始化state和綁定方法),不該順帶完成其餘的附加功能,如統計,動畫等。
該方法的應用場景:根據特定的props變換,來觸發state的更新。如咱們可能會有一個canvas用來表示頁面loading百分比,這個時候就能夠根據傳入的nextProps中的百分比屬性跟當前state的百分比屬性作對比,若不一致,則更新,以下:
componentWillReceiveProps(nextProps) {
if (this.props.percent !== nextProps.percent) {
this.setUpCircle(nextProps.percent);
}
}
複製代碼
該方法的應用場景:使組件可以根據父組件傳入的 props 來更新其自身的 state
在16.4版本以前,只有props更新纔會觸發該方法;但FB在16.4版本作了完善,目前 props 和 state 的更新均會觸發該方法。
使用場景:能夠在根組件的componentWillMount中作一些App的配置。
組件中惟一必須的方法。
render()方法的返回類型目前有 string,number,boolean,null,portal 以及 fragments(帶有key屬性的數組),eg:
render() {
return "string"; //string
return 123; //number
return true; //若是返回的是false,則什麼都不渲染
return null; //若是返回的是null,則什麼都不渲染
return ReactDOM.createPortal(<MyComponent></MyComponent>, domNode);//portal
return [
<div key="unique_1">若是返回的是數組類型</div>,
<span key="unique_2">須要在每一個html元素上加上 key 值</span>, //fragments
<p key="unique_3">不然控制檯會報錯</p>
]
}
複製代碼
須要注意的是,組件在更新的時候,也會觸發 render() 方法,但若是 shouldComponentUpdate() 返回的是false,則在"更新階段",render()方法不會被觸發。
該方法應爲一個純函數,除了作渲染的動做外,不該順帶完成其餘動做(如 setState 等),即應避免 side-effects。
應儘可能僅在 render() 這個生命週期函數中中從 this.props 和 this.state 中讀取數據。
使用場景:總體來講就是作「獲取一些數據」,或者作必需要有 DOM 才能作的設置。
排除了 UNSAFE 的方法。
使用場景:通常用於精確控制組件是否須要從新渲染(這個方法通常用於性能優化),在絕大多數狀況下,每次 state 的更改都應該讓組件從新渲染。
- 只要有一個字段進行了更新,那麼其餘全部字段都會進行更新,這個會減慢頁面速度。(經過shouldComponentUpdate,容許咱們只有當咱們關心的 props 更新的時候,才進行組件的更新)。
- 但須要謹慎使用,由於一旦你本身忘記實現了這個方法,可能會致使你的react組件沒法正常更新。
使用場景:當你實現了 shouldComponentUpdate (返回 true時) 而且在 props 更改時須要執行某些操做的時候,那麼 componentWillUpdate 這個方法仍是有點意義的,但我的認爲,做用不是太大,反卻是增長了理解的複雜度,被刪除也是情理之中。
使用場景:
- 通常在這個方法中進行 RPC 請求。(能夠比較下先前的 props 和當前的 props 是否一致,若一致,則能夠不用請求網絡數據。)
- 若是想要在DOM自身更新以後作一些動做(如從新排列網格等),那麼能夠在這個方法中進行。
排除了 UNSAFE 的方法。
這裏須要注意下,該函數是 will unmount,因此觸發順序上是從父組件到子組件,但釋放順序上,是最內層的子組件先釋放,最終最外層的根組件才釋放。
使用場景:UI中的一些JS錯誤不該該使整個App崩潰,爲了解決這個問題,React16中引入了Error Boundary(錯誤邊界)這個概念,旨在解決容許頁面的部分組件異常但不影響App的渲染。能夠認爲是組件中的 try-catch。而爲了實現這個功能,就須要藉助 componentDidCatch() 這個生命週期函數。
只要該組件內部實現了 componentDidCatch 這個方法就能夠認爲是 ErrorBoundary 組件,如:
...
class MyErrorBoundary extends Component{
...
componentDidCatch(error,info){
...
}
...
}
複製代碼
MyErrorBoundary:
import React, { Component } from 'react';
class MyErrorBoundary extends Component {
state = {
isError: false
}
render() {
if (this.state.isError) {
return (<div>sth wrong here.</div>);
}
return this.props.children;
}
componentDidCatch(error, info) {
console.log(error, info);
this.setState({
isError = true
});
//也能夠作其餘的一些事情,如日誌,異常數統計等
}
}
export default MyErrorBoundary;
複製代碼
App.js
...
render(){
return (
...
<MyErrorBoundary>
<OtherComponent /> </MyErrorBoundary>
...
);
}
...
複製代碼
用來設置state的值。
在16.3開始,FB建議使用setState的異步函數寫法,如: 原先咱們在使用的時候是直接進行賦值:
inputHandler = (e) => {
this.setState({
inputValue: e.target.value //再也不這麼作
});
)
複製代碼
而是改用:
```javascript
inputHandler = (e) => {
let value = e.target.value;
this.setState(() => {
return {inputValue: value}; //使用方法的形式,最終再返回一個對象,這裏須要注意下,這麼寫是異步的,但存在一個問題,即輸入的內容,須要再方法外層先獲取到:如這裏的value。
})
}
```
複製代碼
異步寫法中,該方法提供了一個回調函數,經過該回調函數,能夠確保只有等到setState觸發完成以後,纔會執行回調的方法(另一個能夠確保在setState執行完成以後再執行的點是componentDidUpdate方法),如:
this.setState((prevState, nextState)=>{
...
}, callback);
複製代碼
該方法提供了2個入參,prevState 和 props,前者至關因而 this.state。
只有當setState方法的第一個參數執行完成以後,纔會執行 callback方法。
須要注意的是,由於setState是一個異步方法,因此在賦值的時候須要注意下,若是須要從表單或者其餘地方獲取值賦值給state的某一個屬性,須要先把這個值在setState方法以前賦給一個變量,再在setState方法中使用這個變量。如
let name = e.target.value;
this.setState((prevState, props)=>{
return {
userName : name //不可以直接 userName:e.target.value,異步方法中獲取不到當前上下文
}
}, callback);
複製代碼
補充:對於 setState ,其不必定在調用 setState 的時候就當即觸發這個動做。爲了性能,react會自行判斷,將組件的全部setState在同一個時間點一同執行(批量執行),而非調用一次就執行一次。
每一次 setState 都會致使組件的再次渲染,除非 shouldComponentUpdate 返回 false。