????????一直想寫react的源碼剖析的文章返弹,由于時間成艘、能力等種種原因一直擱置,近來集中研究了一段時間陳屹老師的《深入react技術椂叩担》蔽挠,感覺有種醍醐灌頂之感,因此計劃寫一個系列的文章瓜浸,從react生命周期澳淑、setState機制、diff剖析插佛、react引擎來介紹下這個可愛的框架杠巡。\(^o^)/~
????????本篇剖析的是react的生命周期,介紹在進入正文之前雇寇,說一說react組件的創(chuàng)建吧氢拥。大家都知道,react有自己的語法JSX谢床,以及為人津津樂道的Virtual DOM兄一,那么他們是怎么被編譯成我們業(yè)務中的組件的呢,實際上通過JSX創(chuàng)建的虛擬元素最終會被編譯成調(diào)用React的createElement方法识腿,而createElement只是做了一個簡單的參數(shù)修正出革,返回一個ReactElement對象這個對象就是將我們的JSX與內(nèi)部方法鏈接起來的紐帶,本文不做過多介紹渡讼,有興趣的話可以去讀一讀源碼骂束。
? ? ? ? 對于React組件耳璧,生命周期是他的核心概念之一。React 的主要思想是通過構(gòu)建可復用組件來構(gòu)建用戶界面展箱。所謂組件其實就是有限狀態(tài)機旨枯,通過狀態(tài)渲染對應的界面,且每個組件都有自己的生命周期混驰,它規(guī)定了組件的狀態(tài)和方法需要在哪個階段進行改變和執(zhí)行攀隔。
????????有限狀態(tài)機(FSM),表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的模型栖榨。一般通過狀態(tài)昆汹、事件、轉(zhuǎn)換和動作來描述有限狀態(tài)機婴栽,下面是描述組合鎖狀態(tài)機的模型圖满粗,包括5個狀態(tài)、5個狀態(tài)自轉(zhuǎn)換愚争、6個狀態(tài)間轉(zhuǎn)換和1個復位 RESET 轉(zhuǎn)換到狀態(tài) S1映皆。狀態(tài)機,能夠記住目前所處的狀態(tài)轰枝,根據(jù)當前的狀態(tài)可以做出相應的決策捅彻,并且在進入不同的狀態(tài)時,可以做不同的操作狸膏。通過狀態(tài)機將復雜的關系簡單化沟饥,利用這種自然而直觀的方式可以讓代碼更容易理解。
????????React 正是利用這一概念湾戳,通過管理狀態(tài)來實現(xiàn)對組件的管理贤旷。例如,某個組件有顯示和隱藏兩個狀態(tài)砾脑,通常會設計兩個方法show()和hide()來實現(xiàn)切換幼驶;而 React 只需要設置狀態(tài)setState({ showed: true/false })即可實現(xiàn)。同時韧衣,React 還引入了組件的生命周期概念盅藻。通過它就可以實現(xiàn)組件的狀態(tài)機控制么鹤,從而達到 “生命周期-狀態(tài)-組件” 的和諧畫面窑眯。
通過實驗,我們得到了生命周期執(zhí)行的順序:
1狂秘、當首次掛載組件時硕噩,順序執(zhí)行:getDefaultProps假残,getInitialState,componentWillMount,render辉懒,componentDidMount 阳惹。
2、當卸載組件時眶俩,執(zhí)行componentWillUnmount莹汤。
3、當重新掛在組件的時颠印,按順序執(zhí)行:getInitialState纲岭,componentWillMount,render嗽仪,componentDidMount荒勇。但是不執(zhí)行getDefaultProps
4、當再次渲染組件時闻坚,組件接收到更新狀態(tài),此時按順序執(zhí)行:componentWillReceiveProps兢孝,shouldComponentUpdate窿凤,componentWillUpdate,render跨蟹,componentDidUpdate雳殊。
????????自定義組件(ReactCompositeComponent)的生命周期主要通過三種狀態(tài)進行管理:MOUNTING、RECEIVE_PROPS窗轩、UNMOUNTING夯秃,它們負責通知組件當前所處的狀態(tài),應該執(zhí)行生命周期中的哪個步驟痢艺,是否可以更新 state仓洼。三個狀態(tài)對應三種方法,分別為:mountComponent堤舒、updateComponent色建、unmountComponent,每個方法都提供了兩種處理方法舌缤,will 方法在進入狀態(tài)之前調(diào)用箕戳,did 方法在進入狀態(tài)之后調(diào)用,三種狀態(tài)三種方法五種處理方法国撵,此外還提供兩種特殊狀態(tài)的處理方法陵吸。
? ? ????craetClass是創(chuàng)建自定義組件的入口方法,負責管理生命周期中的getDefaultProps介牙,因此這個方法也將只執(zhí)行一次壮虫,所有實例初始化的props將會被共享。(ps:通過craetClass創(chuàng)建自定義組件耻瑟,利用原型繼承ReactClassComponent父類旨指,按順序合并mixin赏酥,設置初始化defaultProps,返回構(gòu)造函數(shù)谆构,實際上使用es6classes編寫組件時裸扶,class MyCompont extends React.Component實際上就是調(diào)用內(nèi)部方creatClass來創(chuàng)建組件)。下面來詳細剖析一下三個階段:
階段一:MOUNTING
mountComponent負責管理生命周期中的方法:getInitialState搬素、componentWillMount呵晨、render、componentDidMount熬尺。
通過mountComponent掛載組件摸屠,初始化序號、標記等參數(shù)粱哼,判斷是否為無狀態(tài)組件季二,并進行初始化工作,比如初始化props揭措、context等參數(shù)胯舷。利用getInitialState獲取初始化state、初始化更新隊列和更新狀態(tài)绊含。
如果組件中存在componentWillMount桑嘶,則執(zhí)行。如果在componentWillMount中調(diào)用了setState方法躬充,則會進行state合并逃顶,而不是re-render。并且充甚,inst.state = this._processPendingState (inst.props, inst.context) 方法是在componentWillMount之后執(zhí)行的以政,因此componentWillMount方法中的state并不是最新的。
渲染完成后津坑,若存在componentDidMount則調(diào)用妙蔗。這也就是這塊生命周期執(zhí)行順序的由來。?
***PS:mountComponent本質(zhì)上是通過遞歸渲染內(nèi)容的疆瑰,所以父組件的componentWillMount在子組件的componentWillMount之前調(diào)用眉反;父組件的componentDidMount在子組件的componentDidMount之后調(diào)用。
源碼這里就不貼了穆役,感興趣的同學可以自己看書寸五。
階段二:RECEIVE_PROPS
updateComponent負責管理生命周期中的:componentWillReceiveProps、shouldComponentUpdate耿币、componentWillUpdate梳杏、render、componentDidUpdate。
若存在componentWillReceiveProps十性,則執(zhí)行叛溢。如果此時在componentWillReceiveProps中調(diào)用setState,是不會觸發(fā)re-render的劲适,而是會進行state合并楷掉。并且,在componentWillReceiveProps霞势、shouldComponentUpdate烹植、componentWillUpdate中也還是無法獲取到更新后的 this.state,只有在render愕贡、componentDidUpdate中才能獲取到更新后的this.state草雕。
同樣的,updateComponent本質(zhì)上也是是通過遞歸渲染內(nèi)容的固以,所以父組件的componentWillUpdate在子組件的componentWillUpdate之前調(diào)用墩虹;父組件的componentDidUpdate在子組件的componentDidUpdate之后調(diào)用。
這里有一個需要注意的點:嚴禁在shouldComponentUpdate和componentWillUpdate中調(diào)用setState嘴纺,否則會造成循環(huán)調(diào)用败晴,直至耗光瀏覽器內(nèi)存后崩潰,至于為什么栽渴,下節(jié)會在剖析setState的時候為大家解析。
階段三:UNMOUNTING
unmountComponent負責管理生命周期中的unmountComponent稳懒。
首先將狀態(tài)設置為UNMOUNTING闲擦,若存在 componentWillUnmount,則執(zhí)行并重置所有的相關參數(shù)场梆、更新隊列及更新狀態(tài)墅冷,如果此時在 componentWillUnmount 中調(diào)用setState,是不會觸發(fā) reRender或油。更新狀態(tài)為null寞忿,并清除了公共類,完成組件卸載操作顶岸。
*無狀態(tài)組件
這里提一下無狀態(tài)組件腔彰,無狀態(tài)組件只是一個render方法,沒有狀態(tài)辖佣,沒有生命周期霹抛,只是簡單的接收props渲染生成DOM結(jié)構(gòu),是一個純粹為了渲染而生的組件卷谈。相比于有狀態(tài)組件杯拐,它們簡單快捷高效,所以如果有可能的話,請盡量使用無狀態(tài)組件端逼。
最后歸納一下生命周期:
至此朗兵,本篇文章的剖析結(jié)束,下一篇顶滩,解密setState機制余掖,請關注\(^o^)/~