【D1N910】一線大廠React實(shí)踐寶典 React組件(進(jìn)度 3/9)
正常操作,正常分析,大家好,我是D1N910。
在大約兩年前,我購(gòu)買(mǎi)了?騰訊課堂【NEXT】一線大廠 React 實(shí)踐寶典 這門(mén)課。?
因?yàn)槲乙恢被旧鲜鞘褂肰ue來(lái)進(jìn)行前端頁(yè)面的開(kāi)發(fā),但是一直沒(méi)有時(shí)間去實(shí)踐看完。 兩年沒(méi)去看了,真的很慚愧,時(shí)間,唉,過(guò)得真快啊。
為了在這門(mén)課過(guò)期之前看完,所以我要抓緊時(shí)間學(xué)完了。
本系列的專(zhuān)欄記錄了我學(xué)習(xí)?一線大廠React實(shí)踐寶典? 的筆記。?
下面的內(nèi)容版權(quán)歸屬是??騰訊課堂【NEXT】一線大廠 React 實(shí)踐寶典 這門(mén)課。
當(dāng)然,如果我微不足道的筆記也算是我自己的版權(quán),那么也是有我的小小一份。
你可以用來(lái)學(xué)習(xí) React。
但是如果你用來(lái)商業(yè)化盈利,請(qǐng)先獲得??騰訊課堂【NEXT】一線大廠 React 實(shí)踐寶典 制作方的許可。
BTW,作為挖坑狂魔,如果這篇專(zhuān)欄沒(méi)有更新,那么當(dāng)我沒(méi)說(shuō)過(guò)。
這是一個(gè)系列的文章,如果你沒(méi)有看過(guò)前面的內(nèi)容,那么建議你先看一看

這是一個(gè)自己的約定,五一,沖沖沖!
今天,拿下 《React?組件》這個(gè)戰(zhàn)壕,能夠到達(dá)總體進(jìn)度,如果一不小心把 《React 與?DOM》學(xué)完那就是血賺不虧,雖然不是同一個(gè)時(shí)間,但是是同一門(mén)課程,加油,奧利給,兄弟們,干了!

React 組件
這個(gè)課程每節(jié)都會(huì)有一個(gè)很有趣的小劇場(chǎng),通過(guò)拋出一個(gè)問(wèn)題然后接下來(lái)進(jìn)行解答。
這邊提了一個(gè)場(chǎng)景是,想要實(shí)現(xiàn)一個(gè)直播錄音的功能,這個(gè)功能在其他頁(yè)面已經(jīng)有實(shí)現(xiàn)了,想要直接搬過(guò)來(lái)使用,但是代碼雜糅,不是很容易直接搬過(guò)來(lái)使用。
通過(guò)將我們要用功能抽離為組件就可以比較方便地復(fù)用開(kāi)發(fā)了。

組件與組件化
聲明組件
概念:組件就像是一個(gè)函數(shù)。聲明了一個(gè)屬性,就相當(dāng)于聲明了一個(gè)組件。
ES6的組件聲明的寫(xiě)法:

在之前和以后,我們都是會(huì)用這種基于?ES6 的 class 的寫(xiě)法。
每一個(gè)組件都會(huì)通過(guò) render 方法返回一個(gè) React 元素。
使用組件
常見(jiàn)的 React 元素有下面兩種
1、基于 DOM 標(biāo)簽的 React 元素
const element = <div>Hello react</div>
2、基于自定義組件的 React 元素
const element = <Hello />
兩者區(qū)別:自定義組件首字母大寫(xiě)
現(xiàn)在針對(duì)我們之前寫(xiě)的 Hello React 進(jìn)行改造,把 Hello 抽離為組件。

在項(xiàng)目的src文件下,新建一個(gè)文件夾名稱(chēng)叫 components,這個(gè)單詞的意思是組件,一般來(lái)說(shuō),我習(xí)慣把項(xiàng)目的組件統(tǒng)一放到?components 里。
然后再在這個(gè)?components 文件夾下新建一個(gè) Hello 文件夾,Hello 文件夾下新建 index.jsx,內(nèi)容如下:
這里我們只需要引入 React,而不需要引入 ReactDom,因?yàn)槲覀冎恍枰谶@里制作出一個(gè) React 組件,而不需要把它掛載到 Dom 節(jié)點(diǎn)上。

定義好了以后,我們需要在外部引入,直接 import 即可

本小節(jié)完畢, 倉(cāng)庫(kù)
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-1-hello-components
【資料】組件化開(kāi)發(fā)的演變
web1.0 時(shí)代
頁(yè)面代碼主要是后端 JSP / PHP 生成的,前端改了功能后,如果想要看到效果,需要代碼上傳部署后才能夠看到,非常影響效率。而且存在后端邏輯混在前端代碼里的情況。對(duì)于后期來(lái)說(shuō),非常難以維護(hù)。
我本人不幸也算是有幸體驗(yàn)過(guò)這個(gè)開(kāi)發(fā)流程,確實(shí)非??膳?。
后端 MVC 時(shí)代
這個(gè)時(shí)候 Web Server 的架構(gòu)升級(jí)了,有了一些模版引擎,后端同學(xué)可以不用在頁(yè)面模版中揉雜代碼,只需要負(fù)責(zé)給模塊返回對(duì)應(yīng)的數(shù)據(jù)即可,前后端的職責(zé)相對(duì)明確一些了。但是還是要跑前面的流程。
Ajax 時(shí)代
Ajax時(shí)代,可以讓前端更好地掌握主動(dòng)權(quán),這段時(shí)間比之前舒服多了。但是面臨著會(huì)維護(hù)很多代碼的情況。
React.js 帶來(lái)的組件化時(shí)代
React 提供的前端組件化的概念,將很多代碼切割好。

創(chuàng)建 React 組件
組件的屬性
組件的屬性-Props
屬性的傳遞
const element = <Hello name="React" />
JSX 屬性會(huì)被當(dāng)成一個(gè)對(duì)象傳到組件中。這里可以傳入一個(gè)數(shù)字、字符串、對(duì)象、數(shù)組、函數(shù)、組件等等。
屬性的接受
通過(guò)訪問(wèn) this.props 可以獲得組件的屬性拿到值
Props特性
組件的數(shù)據(jù)流,Props 是只讀的。只能從調(diào)用的一方傳入,組件自己只能讀取不能修改。
舉例:
Main 組件 ->?傳遞 title 屬性 -> Header 組件
Main 組件重新 render 的時(shí)候,才會(huì)傳遞新的 title 屬性給到 Header 組件。
以我們之前的 Hello 組件 傳值為例子

React 還提供了 children 的屬性,這個(gè)屬性能夠讓組件拿到父級(jí)傳入的子元素。
改寫(xiě)父文件的 Hello 組件的書(shū)寫(xiě)方式,用包括子元素傳遞。子組件中通過(guò) this.props.children 獲取元素。

對(duì)?Props 進(jìn)行類(lèi)型檢查
為什么需要類(lèi)型檢查?
因?yàn)?JavaScript 是一門(mén)弱類(lèi)型的語(yǔ)言,允許變量類(lèi)型做隱式轉(zhuǎn)換,為了減少錯(cuò)誤,我們可以在 React 中引入類(lèi)型檢查模塊 —— 這樣我們也可以很顯而易見(jiàn)地知道我們的組件接受的參數(shù)類(lèi)型是啥。
在組件中導(dǎo)入包?
import PropTypes from 'prop-types';
(別忘了先 npm install 安裝一下)
然后對(duì)我們的組件添加類(lèi)型檢測(cè)方法

這里的意思是檢測(cè)傳入的需要是字符串參數(shù)。
如果傳參有誤,會(huì)在控制臺(tái)中拋出報(bào)錯(cuò)

所有能檢測(cè)的類(lèi)型在?prop-types 的 npm 包文檔說(shuō)明中
https://www.npmjs.com/package/prop-types
我們也可以通過(guò)配置?defaultProps 為 Props 定義默認(rèn)值

函數(shù)定義組件
函數(shù)定義組件的例子

函數(shù)定義組件 與 類(lèi)組件 的區(qū)別(內(nèi)容大部分參考自原文)
1、函數(shù)式組件不會(huì)被實(shí)例化,整體渲染性能得到提升
函數(shù)式組件被精簡(jiǎn)為只有render方法, 沒(méi)有實(shí)例化的過(guò)程,不需要分配多余的內(nèi)容,讓性能得到提升。
本身沒(méi)有this,使用 Ref 等模塊(后面會(huì)說(shuō))時(shí)與類(lèi)組件也會(huì)有所區(qū)別。
2、函數(shù)式組件沒(méi)有狀態(tài)
函數(shù)式組件本身沒(méi)有自己的內(nèi)部狀態(tài) state,數(shù)據(jù)依賴(lài)與 props 的傳入, 類(lèi)似剛剛的?
getH1(this.props),所以又稱(chēng)為無(wú)狀態(tài)組件。
3、函數(shù)式組件無(wú)訪問(wèn)生命周期(后面會(huì)說(shuō))的方法
函數(shù)式組件是不需要組件生命周期管理,所以底層實(shí)現(xiàn)這種形式的組件時(shí)是不會(huì)實(shí)現(xiàn)組件的生命周期方法。
4、何時(shí)該使用函數(shù)式組件
函數(shù)式組件相比類(lèi)組件,擁有更好的性能和更簡(jiǎn)單的職責(zé),十分適合分割原本龐大的組件,未來(lái) React 也會(huì)對(duì)函數(shù)式組件進(jìn)行一系列的優(yōu)化,譬如無(wú)意義檢查和內(nèi)存分配領(lǐng)域相關(guān)的優(yōu)化。所以只有有可能,盡量使用函數(shù)式組件。
本小節(jié)完畢,相關(guān)代碼如下
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-5-props

組件的組合與提取(設(shè)計(jì)組件)
組件的拆分
舉例,?發(fā)現(xiàn) Main 組件是由 組件 A、組件 B 和組件 C 組成的,其中發(fā)現(xiàn)組件 C 里面有一個(gè)組件 D。
這個(gè)自上而下的過(guò)程就是組件的 拆分 / 細(xì)化。
這個(gè)自下而上的過(guò)程就是組件的組合。
舉例子,下面有一個(gè)普通的博客網(wǎng)頁(yè)

提取組件后,結(jié)構(gòu)長(zhǎng)下面這樣

最外層的父文件

header 組件

section 組件

最后查看網(wǎng)頁(yè)能夠正常展示結(jié)果~這樣我們就完成了一個(gè)組件的拆分。

本小節(jié)結(jié)束,對(duì)應(yīng)代碼 如下
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-6-design-components
本節(jié)對(duì)應(yīng)資料
SOLID 原則(基本上都是源自資料)
SOLID是五個(gè)面向?qū)ο缶幊痰闹匾瓌t的縮寫(xiě)。另外,它也是每個(gè)開(kāi)發(fā)者必備的基本知識(shí)。了解并應(yīng)用這些原則能讓你寫(xiě)出更優(yōu)質(zhì)的代碼,變成更優(yōu)秀的開(kāi)發(fā)者。這個(gè)五個(gè)原則分別是:
SRP(單一責(zé)任原則)
OCP(開(kāi)放封閉原則)
LSP(里氏替換原則)
ISP(接口分離原則)
DIP(依賴(lài)倒置原則)
單一責(zé)任原則(SRP)
* 一個(gè)類(lèi)應(yīng)該只有一個(gè)引起改變的原因
這個(gè)原則意味著一個(gè)類(lèi)只應(yīng)承擔(dān)一個(gè)職責(zé)。如果我們的類(lèi)承擔(dān)的職責(zé)多于一個(gè),那么我們的代碼就具有高度的耦合性,任何對(duì)它的修改都會(huì)可能影響到多個(gè)地方,難以維護(hù)。遵循該原則可以降低代碼的耦合性,提高代碼的可維護(hù)性。
開(kāi)閉原則(OCP)
* 軟件實(shí)體(類(lèi),模塊,函數(shù)等)應(yīng)該對(duì)拓展開(kāi)放,對(duì)修改封閉。
根據(jù)這一原則,一個(gè)軟件實(shí)體能很容易地?cái)U(kuò)展新功能而不必修改現(xiàn)有的代碼。這個(gè)原則的實(shí)現(xiàn)要點(diǎn)在于,使用合理的抽象構(gòu)建一個(gè)實(shí)體的框架,功能細(xì)節(jié)能在實(shí)現(xiàn)中擴(kuò)展。這個(gè)原則要求我們?cè)谠O(shè)計(jì)軟件的時(shí)候要預(yù)知未來(lái)可能發(fā)生的改變,合理的抽象實(shí)體框架,遵循該原則可以提高代碼的可維護(hù)性和復(fù)用性。
里氏替換原則(LSP)
* 程序里的對(duì)象可以在不影響系統(tǒng)正常運(yùn)作的條件下被它的子類(lèi)實(shí)例所替換
簡(jiǎn)單來(lái)說(shuō),這個(gè)原則要求我們?cè)诶^承一個(gè)父類(lèi)的時(shí)候,不能修改父類(lèi)中已經(jīng)實(shí)現(xiàn)的方法。遵循該原則可以提高代碼復(fù)用性和穩(wěn)定性,降低系統(tǒng)出現(xiàn)問(wèn)題的出現(xiàn)幾率。
接口隔離原則(ISP)
* 一個(gè)類(lèi)所定義的接口中不應(yīng)存在該類(lèi)不需要用到的方法
這個(gè)原則要求接口應(yīng)當(dāng)被設(shè)計(jì)成多個(gè)專(zhuān)用的接口而不是一個(gè)通用接口。不遵循這個(gè)原則意味著我們?cè)趯?shí)現(xiàn)一個(gè)接口的時(shí)候會(huì)依賴(lài)很多我們并不需要的方法,但又不得不去定義。遵循該原則可以降低代碼的耦合度,后期更易于代碼重構(gòu)。
依賴(lài)倒轉(zhuǎn)原則(DIP)
* 高層次的模塊不應(yīng)該依賴(lài)于低層次的模塊,二者都應(yīng)該依賴(lài)于抽象。抽象不應(yīng)該依賴(lài)于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴(lài)于抽象。
這個(gè)原則簡(jiǎn)單來(lái)說(shuō),就是面向接口變成,它要求一個(gè)特定的類(lèi)不應(yīng)該直接依賴(lài)于另外一個(gè)類(lèi),但是可以依賴(lài)于這個(gè)類(lèi)的抽象(接口)。使用該原則可以降低類(lèi)與類(lèi)的耦合度,提高我們代碼的復(fù)用性。
總結(jié)
遵循 SOLID 原則可以幫助我們構(gòu)建高質(zhì)量、易擴(kuò)展、易維護(hù)的軟件系統(tǒng),但過(guò)分的設(shè)計(jì)會(huì)讓簡(jiǎn)單的問(wèn)題復(fù)雜化,所以我們開(kāi)發(fā)應(yīng)當(dāng)是從實(shí)際出發(fā),靈活應(yīng)用 SOLID 原則。
(手打了一遍,希望后面開(kāi)發(fā)組件的時(shí)候能夠多多用到)

組件的數(shù)據(jù)流
小劇場(chǎng):一個(gè)復(fù)雜的組件的屬性不能都依賴(lài)于外部吧,自己內(nèi)部能夠數(shù)據(jù)管理嗎。React 有局部狀態(tài)屬性 State,可以在組件中單獨(dú)進(jìn)行維護(hù)。
組件的狀態(tài) - State
props 和 state 是組件中存儲(chǔ)數(shù)據(jù)的主要方式。
首先來(lái)理解下什么是狀態(tài)。為什么組件會(huì)擁有狀態(tài)。
簡(jiǎn)而言之,組件就是一個(gè)狀態(tài)機(jī),不同的狀態(tài)會(huì)帶給組件不同的表現(xiàn)。
當(dāng)一個(gè)組件擁有不同的狀態(tài)( state )后,它就會(huì)展示不同的樣式 (view)。
state -> view
比如我們有一個(gè) Button 組件,它有狀態(tài)1、狀態(tài)2和狀態(tài)3,分別代表著它是黃色、紅色和綠色。
State 的聲明和使用
下面的代碼用 ES6 的寫(xiě)法,在?constructor 這個(gè)類(lèi)的構(gòu)造函數(shù)中,初始化了狀態(tài)?this.state。
想要獲取這個(gè)值,和獲取props一樣,可以使用 this.state.name

State 更新
以一個(gè)時(shí)鐘組件為例子。
定一個(gè) clock 屬性,初始值是 new Date(),在函數(shù)?componentDidMount (馬上就會(huì)介紹這個(gè)函數(shù),現(xiàn)在先說(shuō)明這是在組件掛載時(shí)會(huì)執(zhí)行的方法)定義了一個(gè)定時(shí)器,每隔1s 去調(diào)用 this.setState() 方法更新 state 的 clock 屬性,react 檢測(cè)到了這個(gè)變化后,就會(huì)執(zhí)行 render 函數(shù)重新去渲染更新視圖。
相對(duì)于設(shè)置了定時(shí)器,我們也在?componentWillUnmount(組件將要卸載的時(shí)候)?定義了一個(gè)清除定時(shí)器的方法。
代碼和效果如下

本小節(jié)完畢,相關(guān)代碼在此
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-7-state
【資料】正確地使用組件局部狀態(tài)
(又到了無(wú)情的打字機(jī)器人時(shí)間,內(nèi)容基本上都是照搬自原文的,也有些是我個(gè)人的內(nèi)容)
你使用State的姿勢(shì)正確了嗎?React 的核心思想是組件化的思想,而 State 正是組件中最重要的部分。組件狀態(tài)的正確使用,可以幫助我們構(gòu)造出可維護(hù)性更佳的組件。
正確地定義組件 State
State 是組件 UI 的數(shù)據(jù)模型,UI 的改變可以從 State 的變化中反映,而 State 中所有狀態(tài)的改變都會(huì)引起組件 UI 的變化。一個(gè)變量是否應(yīng)該被設(shè)置為狀態(tài),可以從以下兩點(diǎn)進(jìn)行判斷:
這個(gè)變量能否通過(guò)其他狀態(tài)或者 props 計(jì)算得到?如果可以,則其不應(yīng)該作為一個(gè)狀態(tài)。
該變量是否在 renader 方法中被使用,如果沒(méi)有,其不該作為一個(gè)狀態(tài)
簡(jiǎn)單來(lái)說(shuō),組件的狀態(tài)一定要體現(xiàn)到 UI 的更新,且其值是唯一,無(wú)法通過(guò)計(jì)算得到。
不要直接更新 State
以下代碼不會(huì)重新渲染組件:
// 錯(cuò)誤
this.state.comment = 'Hello';
應(yīng)當(dāng)使用 setState 方法:
// 正確
this.setState({comment: 'Hello'});
值得一提的是,構(gòu)造函數(shù)是唯一能夠初始化 this.state 的地方
狀態(tài)更新可能是異步的
React 可能會(huì)將多個(gè) setState 調(diào)用合并為一個(gè)調(diào)用來(lái)提高性能,因此 this.props 和this.state 可能是異步更新的,你不應(yīng)該靠他們的值來(lái)計(jì)算下一個(gè)狀態(tài)。
例如,此代碼可能無(wú)法更新計(jì)數(shù)器:
// 錯(cuò)誤
this.setState({
????counter: this.state.counter + this.props.increment
})
在這種強(qiáng)況下,我們使用 setState 方法時(shí)應(yīng)當(dāng)接收一個(gè)函數(shù)而不是一個(gè)對(duì)象。該函數(shù)將舊狀態(tài)作為第一個(gè)參數(shù),將此次更新被應(yīng)用時(shí)的 props 作為第二個(gè)參數(shù),如下:
// 正確
this.setState((prevState, props) => ({
????counter: prevState.counter + props.increment
}))
關(guān)于 setState 的淺合并
當(dāng)你調(diào)用 setState 方法時(shí),React 將你提供的對(duì)象合并到當(dāng)前狀態(tài)。
例如我們的組件中包含多個(gè)狀態(tài):
constructor(props) {
? ?super(props);
? ?this.state = {
? ? ?clock: new Date(),
????? name: 'Da Ben'
? ?}
?}
我們可以獨(dú)立的更新其中一個(gè)狀態(tài)屬性:
componentDidMount() {
? ? ?this.setState({
????? ? ? name: 'Da Ben 2'
? ? })
?}
這里的合并是淺合并,也就是說(shuō) this.setState({name}) 完整保留了 this.state.clock,但完全替換了 this.state.name。
狀態(tài)提升
在 React 中,狀態(tài)分享是通過(guò)將 state 數(shù)據(jù)提升至離需要這些數(shù)據(jù)的組件最近的父組件來(lái)完成的。這就是所謂的狀態(tài)提升。
在 React 應(yīng)用中,遵循單一數(shù)據(jù)源原則。簡(jiǎn)單來(lái)說(shuō),如果兩個(gè)或者多個(gè)組件依賴(lài)于相同的數(shù)據(jù),那么這些數(shù)據(jù)應(yīng)當(dāng)放到離需要它們的最近的父組件中,在應(yīng)用中保持自上而下的數(shù)據(jù)流。這樣的做法可以幫助我們更快的定位 bug,因?yàn)榻M件的狀態(tài)只有組件自身可以操作,把可能產(chǎn)生 bug 的范圍大大縮減了。
實(shí)際上就是放到 props 中。

組件的生命周期
小劇場(chǎng)
聰明男:剛剛我們有用到生命周期,那么 React 的生命周期具體是什么呢?
懵懂男:我理解為是一個(gè)組件從創(chuàng)建到銷(xiāo)毀的過(guò)程。
聰明男:你理解得不錯(cuò)。生命周期主要分為三個(gè),分別為組件創(chuàng)建時(shí)、組件更新時(shí)還有組件銷(xiāo)毀時(shí)。并且 React 還為我們提供了對(duì)應(yīng)的生命周期函數(shù)。
捧哏男:那我們什么時(shí)候需要用到這些生命周期函數(shù)呢?
聰明男:舉個(gè)??,componentDidMount 代表我們的組件已經(jīng)掛載到了 DOM上,我們經(jīng)常會(huì)在這個(gè)函數(shù)里做一些網(wǎng)絡(luò)請(qǐng)求,還有事件訂閱等操作。
懵懂男:這么說(shuō),生命周期很重要的吧
聰明男:Yep
組件的生命周期
之前講了組件的屬性 props 和 狀態(tài) state。組件的生命周期是第三個(gè)非常重要的知識(shí)點(diǎn)。
組件的生命周期分為三個(gè)階段,每個(gè)階段有對(duì)應(yīng)的生命周期,不過(guò)下面介紹的可能和之前你有接觸過(guò)的生命周期不太一樣,因?yàn)檫@里是 比較新的React 新增了一些生命周期(下面會(huì)標(biāo)綠),又把一些生命周期設(shè)置為預(yù)備廢棄狀態(tài)。
下面針對(duì)的是 v16.4.0 React 生命周期展開(kāi)詳解
(1)裝載過(guò)程
getDerivedStateFromProps,componentDidMount
(2)更新過(guò)程
getDerivedStateFromProps、getSnapshotBeforeUpdate、
shouldComponentUpdate、componentDidUpdate
(3)卸載過(guò)程
componentWillUnmount
生命周期的流程
下面請(qǐng)看參考視頻的,生命周期流程概覽。

創(chuàng)建時(shí)(組件初始化)

getDerivedStateFromProps
組件創(chuàng)建之后執(zhí)行(DOM?未掛載)。
componentDidMount
組件完成實(shí)例化后執(zhí)行。
此時(shí)我們的組件已經(jīng)掛載到實(shí)際的 dom 上。
這是個(gè)很常用的方法。
經(jīng)常會(huì)在這里進(jìn)行這些操作:
執(zhí)行 Ajax 請(qǐng)求
執(zhí)行事件訂閱
嘗試在之前的時(shí)鐘方法里使用執(zhí)行這段創(chuàng)建時(shí),打印出對(duì)應(yīng)的內(nèi)容

getDerivedStateFromProps
聲明加?static,說(shuō)明這個(gè)函數(shù)和組件的狀態(tài)無(wú)關(guān),只要給一個(gè)固定的 props 和 固定的 state 就會(huì)有固定的產(chǎn)出。
在沒(méi)有對(duì)?state 有進(jìn)行更新,則返回?null。
但如果我們想對(duì)?state 有做修改,比如根據(jù) props 傳入的小時(shí)來(lái)設(shè)置 clock 的小時(shí),可以這么做:
props 的屬性值 hour 傳入 16。

然后再在 getDerivedStateFromProps 里進(jìn)行獲取設(shè)置即可

組件更新時(shí)的生命周期
在 新屬性、setState、forceUpdate 的時(shí)候會(huì)執(zhí)行組件更新。

getDerivedStateFromProps
創(chuàng)建的時(shí)候也有,根據(jù)屬性設(shè)置 state,可以獲得計(jì)算好的新的state
shouldComponentUpdate
在這里可以判斷組件是否重新渲染,如果返回 true 則往下執(zhí)行。否則中斷,告訴 react 本次更新不會(huì)涉及頁(yè)面上的渲染,可以節(jié)省開(kāi)銷(xiāo)。
getSnapshotBeforeUpdate
這時(shí)候dom已經(jīng)渲染好了,但是我們還沒(méi)有更新到實(shí)際 dom 上,這里我們的返回值可以作為下一生命周期函數(shù) componentDidUpdate 的第三個(gè)參數(shù)。
有一個(gè)快照的作用,記錄下我們預(yù)掛載但是還沒(méi)有掛載的狀態(tài)。
componentDidUpdate
組件完成更新后執(zhí)行。
下面還是用我們之前的鐘表組件來(lái)嘗試執(zhí)行上面的生命周期,這里我們把更新時(shí)間調(diào)為 5 秒更新一次,這樣能夠比較好看得到生命周期。

組件掛載時(shí)
componentWillUnmount
在組件被卸載和銷(xiāo)毀之前被調(diào)用。我們可以在該方法里做清理工作,例如解綁定時(shí)器,取消網(wǎng)絡(luò)請(qǐng)求,清理任何在 componentDidMount 環(huán)節(jié)創(chuàng)建的 DOM 元素,取消事件訂閱。
可以設(shè)定一個(gè)定時(shí)器讓時(shí)鐘組件掛載,代碼如下

異常處理的周期函數(shù)
componentDidCatch
錯(cuò)誤邊界是一個(gè) React 組件。錯(cuò)誤邊界組件捕捉發(fā)生在子組件樹(shù)中任意地方的 JavaScript 錯(cuò)誤,打印錯(cuò)誤日志,并且顯示回退的用戶界面。錯(cuò)誤邊界可以捕捉組件渲染期間、生命周期方法中和子組件構(gòu)造函數(shù)中的錯(cuò)誤。
如果定義了這一生命周期方法,一個(gè)類(lèi)組件將成為一個(gè)錯(cuò)誤邊界組件。我們可以在錯(cuò)誤邊界組件捕獲到 JavaScript 錯(cuò)誤的時(shí)候,顯示回退的用戶界面。
因?yàn)榭梢圆蹲降阶咏M件的報(bào)錯(cuò),所以我們一般只需要聲明一次錯(cuò)誤邊界組件,就可以在整個(gè)應(yīng)用程序中使用。

本小節(jié)完畢,相關(guān)代碼在此
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-8-lifecycle

高階組件
什么是高階組件?高階組件就是一個(gè)函數(shù),它的特點(diǎn)是傳入一個(gè)組件,然后又返回一個(gè)組件。
Fn(組件) = 高階組件
高階組件的作用:實(shí)現(xiàn)了組件邏輯的重用。
舉個(gè)??
A播放器有功能:播放、暫停、上一首,下一首
B播放器有功能:播放、暫停、上一首,下一首
A、B播放器除了展示樣式、部分特殊功能以外,大部分功能都是一樣的。
可以把 播放、暫停、上一首,下一首 這些播放邏輯抽離出來(lái)整合到一個(gè)播放器高階組件內(nèi)。
用這個(gè)播放器高階組件 分別針對(duì) A 播放器、B 播放器進(jìn)行整合添加功能,然后輸出帶有播放器功能的?A 播放器 和 B 播放器,實(shí)現(xiàn)一個(gè)邏輯的重用。
注意
高階組件不會(huì)改變?cè)冀M件,它只是在原始組件外面包了一個(gè)殼
高階組件是對(duì)原始組件能力的加強(qiáng)
下面針對(duì)我們之前的時(shí)鐘進(jìn)行一個(gè)高階組件的封裝。
為了方便用高階組件,先把原來(lái)的代碼改造一下,把 export default 放到下面,實(shí)際上效果沒(méi)差。

我們做的這個(gè)高階組件的功能,是讓組件上面都會(huì)有 “你好”
下面把這個(gè)方法寫(xiě)好,這個(gè)思路挺簡(jiǎn)單的,感覺(jué)也蠻好理解。注釋我打滿了。

使用高階組件,在我們的 main/index.jsx 中引入 Hello 方法,然后導(dǎo)出 Hello(Main) 即可,這時(shí)候我們也能夠看到導(dǎo)出的效果有相關(guān)的內(nèi)容。

后繼也可以在這個(gè)高階組件上加各種通用的方法、組件內(nèi)容。
本小節(jié)完畢,代碼如下
https://github.com/D1N910/learn-react/tree/master/Chapter2/demo-9-higher-order-component
上面的高階組件只是圖一樂(lè),真要說(shuō)明白了,還是得看我們的裝飾者模式。
【資料】裝飾者模式
裝飾模式可以在不改變一個(gè)對(duì)象本身功能的基礎(chǔ)上給對(duì)象增加額外的新行為。裝飾模式就是一種用于替代繼承的技術(shù),它通過(guò)一種無(wú)須定義子類(lèi)的方式來(lái)給對(duì)象動(dòng)態(tài)增加職責(zé),使用對(duì)象之間的關(guān)聯(lián)關(guān)系取代類(lèi)之間的繼承關(guān)系。在裝飾模式中引入裝飾類(lèi),在裝飾類(lèi)中既可以調(diào)用待裝飾的原有類(lèi)的方法,還可以增加新的方法,以擴(kuò)充原有類(lèi)的功能。
例子:
假設(shè)有一個(gè)老八類(lèi),他負(fù)責(zé)給所有吃的加腐乳
var Laoba = function () {}
Laoba.prototype.work = funciton () {
????console.log('加腐乳');
}
這個(gè)時(shí)候,大家都希望他在加腐乳的同時(shí),還要加臭豆腐。
在非裝飾者模式下,我們可以這樣給老八添加“加臭豆腐”的工作
var Laoba = function () {}
Laoba.prototype.work = funciton () {
????console.log('加腐乳');
? ??console.log('加臭豆腐');
}
這個(gè)時(shí)候其實(shí)是修改了原有類(lèi)的代碼,會(huì)導(dǎo)致所有該類(lèi)的實(shí)例都可能發(fā)生改變(可能不是同一個(gè)時(shí)間,但是是同一個(gè)地點(diǎn),不同的老八不用加臭豆腐呢),同時(shí)這個(gè)也違背了開(kāi)放-封閉原則。
我們可以這樣改動(dòng)
// 原始的老八類(lèi)
var Laoba = function() {}
Laoba.prototype.work = function () {
? ? console.log('加腐乳');
};
// 裝飾類(lèi)
var TicketLaoba = function(laoba) {
????this.laoba =?laoba
}
TicketLaoba.prototype.work = function () {
? ? this.laoba.work();
? ? console.log('加臭豆腐');
};
var laoba = new?TicketLaoba(new Laoba())
laoba.work()

我們?cè)谝粋€(gè)對(duì)象中放入另外一個(gè)對(duì)象,形成一個(gè)聚合對(duì)象,被放入的對(duì)象(即被裝飾的對(duì)象)本身沒(méi)有任何的改變。
使用場(chǎng)景
當(dāng)我們需要擴(kuò)展一個(gè)類(lèi)的功能,且使用繼承會(huì)很復(fù)雜的時(shí)候,我們可以使用裝飾者模式進(jìn)行功能的擴(kuò)展。
優(yōu)點(diǎn)
對(duì)于擴(kuò)展一個(gè)對(duì)象的功能,裝飾模式比繼承更加靈活性,不會(huì)導(dǎo)致類(lèi)的個(gè)數(shù)急劇增加。
可以通過(guò)一種動(dòng)態(tài)的方式在運(yùn)行時(shí)選擇不同的具體裝飾類(lèi),從而實(shí)現(xiàn)不同的行為。
可以對(duì)一個(gè)對(duì)象進(jìn)行多次裝飾,通過(guò)使用不同的具體裝飾類(lèi)以及這些裝飾類(lèi)的排列組合,可以創(chuàng)造出很多不同行為的組合,得到功能更為強(qiáng)大的對(duì)象。
缺點(diǎn)
被裝飾多次的對(duì)象,報(bào)錯(cuò)需要逐級(jí)排查,較為繁瑣。
使用裝飾者模式會(huì)產(chǎn)生許多小對(duì)象,這些對(duì)象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類(lèi)或者屬性值有所不同,大量的小對(duì)象會(huì)占用更多的系統(tǒng)資源,影響程序性能。

Part 2:React組件 學(xué)習(xí)完畢
總體學(xué)習(xí)進(jìn)度(3/9)
一路干到 03:30,媽耶,明天不能摸魚(yú)了
上面的內(nèi)容都是學(xué)習(xí)自 騰訊課堂 【NEXT】學(xué)院?一線大廠React實(shí)踐寶典?
個(gè)人學(xué)習(xí)筆記
加油加油加油
蛋糕