React是一個(gè)JavaScript語(yǔ)言的工具庫(kù)奢赂,起源于Facebook的一個(gè)內(nèi)部項(xiàng)目,最初用來(lái)構(gòu)建Instagram的網(wǎng)站呛哟,并于2013年5月開源苞俘。
安裝
對(duì)于React初學(xué)者而言,React官方提供了一個(gè)工具——create-react-app计维,目的是將開發(fā)者人員從配置中解放出來(lái)袜香,從而能快速的進(jìn)入到React應(yīng)用的開發(fā)中。
具體安裝過(guò)程如下:
npm install -g create-react-app ? 全局安裝 creat-react-app 命令
create-react-app ? my-app ? ? ? ? ?使用命令在當(dāng)前目錄下創(chuàng)建一個(gè)名為my-app的react應(yīng)用
cd my-app ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 進(jìn)入my-app項(xiàng)目文件夾
npm-start ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 執(zhí)行該命令
之后會(huì)啟動(dòng)一個(gè)開發(fā)者模式的服務(wù)器鲫惶,同時(shí)瀏覽器自動(dòng)打開一個(gè)網(wǎng)頁(yè)蜈首,指向http://localhost:3000/
界面如下圖所示:
第一個(gè)React應(yīng)用就這樣誕生了~
生成的文件夾目錄結(jié)構(gòu),如下圖所示:
開發(fā)過(guò)程中欠母,我們主要關(guān)注src目錄中的內(nèi)容欢策,而我們所創(chuàng)建項(xiàng)目的入口便是src/index.js文件,這里面的代碼做了這樣一件事赏淌,它渲染了一個(gè)名為App的組件踩寇,將其掛在到了id為root的DOM節(jié)點(diǎn)上。
同時(shí)我們?cè)賮?lái)看看在src/App.js中六水,App組件是怎樣創(chuàng)建的:
可以看到代碼的第一行我們從react庫(kù)中引入了React和Component俺孙,Component作為所有組件的基類辣卒,提供了很多組件共有的功能,之后創(chuàng)建了一個(gè)名為App的組件類睛榄,然后通過(guò)export default App將其導(dǎo)出荣茫,并在在src/index.js中通過(guò)import導(dǎo)入,最后加以使用场靴。
UI=render(data)
React的理念歸結(jié)為一個(gè)公式:UI=render(data)
用戶看到的UI界面啡莉,是一個(gè)函數(shù)——render的執(zhí)行結(jié)果,只接受數(shù)據(jù)——data作為參數(shù)憎乙。這是一個(gè)純函數(shù)票罐,即輸出只依賴于輸入的函數(shù),兩次函數(shù)的調(diào)用如果輸入相同泞边,那么輸出也絕對(duì)相同该押。如此一來(lái),最終的用戶界面阵谚,在render函數(shù)確定的情況下完全取決于輸入數(shù)據(jù)蚕礼。
因此對(duì)于開發(fā)者而言,重要的是區(qū)分梢什,哪些是render奠蹬,哪些是data,想要改變UI嗡午,要做的就是更新data
——《深入淺出React和Redux》
Virtual DOM
在上一節(jié)中囤躁,我們似乎很容易得出這樣一個(gè)結(jié)論,每次更新UI界面荔睹,react都要進(jìn)行重新渲染狸演,這樣會(huì)不會(huì)使得效率低下呢?
事實(shí)上并不會(huì)這樣僻他,React利用Virtual DOM讓每次渲染都只渲染最少的DOM元素宵距。Virtual DOM是對(duì)DOM樹的抽象,它并不觸及瀏覽器部分吨拗,只是存在于JavaScript空間的樹形結(jié)構(gòu)满哪,每次在渲染React組件,React會(huì)對(duì)前后兩次產(chǎn)生的Virtual DOM進(jìn)行比較劝篷,最后只有發(fā)生了改變的地方會(huì)被重新渲染哨鸭。
總而言之,React利用函數(shù)式編程的思維來(lái)解決用戶界面渲染的問(wèn)題携龟,強(qiáng)制所有組件都以數(shù)據(jù)驅(qū)動(dòng)渲染的模式進(jìn)行開發(fā)兔跌。
JSX
要學(xué)習(xí)react首先就要了解JSX,它是JavaScript的一種擴(kuò)展語(yǔ)法峡蟋,使我們能夠在JavaScript中編寫類似HTML的代碼坟桅。
JSX 的基本語(yǔ)法規(guī)則:遇到 HTML 標(biāo)簽(以<開頭),就用 HTML 規(guī)則解析蕊蝗;遇到代碼塊(以{開頭)仅乓,就用 JavaScript 規(guī)則解析。
我們可以用{}將任意的JavaScript表達(dá)式嵌入到JSX中蓬戚。
JSX注釋也很簡(jiǎn)單夸楣,一般用花括號(hào)包圍:
{/* 這里是注釋 */}
React組件
React允許將代碼封裝成組件,然后像插入普通HTML標(biāo)簽?zāi)菢幼愉觯迦胧褂梦覀冏远x的組件豫喧。自定義的組件要以大寫字母開頭,這樣才能被識(shí)別為React組件幢泼。
組件也可以在標(biāo)簽中插入任意屬性紧显,特別注意:
class屬性需要寫成className,for屬性需要寫成htmlFor缕棵,這是因?yàn)閏lass和for是 JavaScript 的保留字孵班。對(duì)于屬性的訪問(wèn),可以通過(guò)this.props.xxx來(lái)訪問(wèn)對(duì)應(yīng)的xxx屬性招驴。
每個(gè)組件必須有一個(gè)render方法篙程,用于輸出組件自身,同時(shí)輸出的組件只能包含一個(gè)頂層標(biāo)簽别厘。
舉個(gè)例子:
在上面的例子中虱饿,我們創(chuàng)建了一個(gè)名為Demo的React組件,用來(lái)在界面輸出Hello触趴,my name is xxx的信息氮发。而this.props.name就是對(duì)組件調(diào)用時(shí)傳入的name屬性的訪問(wèn)。最終界面輸出如下圖所示:
props & state
React組件的數(shù)據(jù)分為兩種——props和state雕蔽。其中props是組件的對(duì)外接口折柠,state則是組件的內(nèi)部狀態(tài)。它們?nèi)魏我粋€(gè)的改變都會(huì)引發(fā)組件的重新渲染批狐。
props
props是從外部傳遞給組件的數(shù)據(jù)扇售,在上一節(jié)的例子中,name就是傳遞給Demo組件的一個(gè)外部props嚣艇,同時(shí)在組件內(nèi)部承冰,通過(guò)this.props.nam訪問(wèn),這樣我們就能很輕易的將外部的數(shù)據(jù)傳遞給組件內(nèi)部食零。那相反困乒,如果我們想要把內(nèi)部的數(shù)據(jù)傳遞給外部該怎么辦呢?
同樣也是使用props贰谣,因?yàn)閜rops的類型不限于純數(shù)據(jù)娜搂,它還可以是函數(shù)迁霎,所以我們可以定義一個(gè)函數(shù)類型的props,相當(dāng)于父組件交給了自組件一個(gè)回調(diào)函數(shù)百宇。自組件在適當(dāng)?shù)臅r(shí)候調(diào)用這個(gè)函數(shù)考廉,并傳入必要的參數(shù),父組件接收到參數(shù)后携御,對(duì)參數(shù)進(jìn)行相應(yīng)的邏輯處理昌粤。
舉個(gè)例子:
在Parent父組件中,我們定義了一個(gè)changeResult的函數(shù)啄刹,它接收一個(gè)參數(shù)涮坐,最后計(jì)算出參數(shù)的二次冪,將結(jié)果result顯示在界面中誓军。同時(shí)袱讹,接收的這個(gè)參數(shù)是來(lái)自于Child組件,父組件在調(diào)用子組件時(shí)谭企,將changeValue這個(gè)方法作為子組件的props傳給了子組件廓译。
在Child子組件中,點(diǎn)擊button會(huì)使得子組件中value值加1债查,并在此時(shí)調(diào)用this.props.changeValue非区,將新的value值傳給父組件Parent。
最終的效果如下圖所示:
protoType可以用來(lái)規(guī)范組件的接口盹廷,定義自己可以接收哪些類型或者符合哪些規(guī)則的prop征绸。以上面的Child組件來(lái)說(shuō),我們要求它接收的changeReault是一個(gè)函數(shù):
Child.propTypes = {
changeResult: PropTypes.func
};
上面的代碼要求changeResult這個(gè)prop必須是個(gè)函數(shù)的類型俄占。
state
state代表組件的內(nèi)部狀態(tài)管怠,因?yàn)榻M件不能修改傳入的props,所以為了記錄自身的狀態(tài)變化就得用到state缸榄。
組件的state通常在組件的構(gòu)造函數(shù)中初始化渤弛,且state必須是一個(gè)對(duì)象。
constructor(props){
? ? super(props);
? ? this.state = {
? ? ? ? value: 0
? ? }
}
React還提供了一個(gè)defaultProps功能甚带,用來(lái)制定默認(rèn)的prop值她肯,在特定的props值不是必須且沒(méi)有被傳入的情況下,會(huì)使用這個(gè)指定的默認(rèn)值鹰贵。
Child.defaultProps = {
? ? changeResult: f => f ? ? ?//默認(rèn)是一個(gè)什么都不做的函數(shù)
}
對(duì)state的修改不能直接通過(guò)this.state.xxx來(lái)修改晴氨,需要調(diào)用this.setState({xxx: newValue});來(lái)修改。因?yàn)閠his.setState會(huì)驅(qū)動(dòng)組件進(jìn)行重新渲染碉输,而如果直接用this.state.xxx來(lái)修改籽前,并不能觸發(fā)。
React組件的生命周期
React定義了組件的生命周期,可分為一下三個(gè)過(guò)程:
裝載過(guò)程:Mount —— 組件第一次在DOM樹中渲染的過(guò)程
更新過(guò)程:Update —— 組件被重新渲染的過(guò)程
卸載過(guò)程:Unmount —— 組件從DOM中刪除的過(guò)程
在不同的生命周期中枝哄,React會(huì)一次調(diào)用組件的一些函數(shù)肄梨,這些函數(shù)被稱為生命周期函數(shù)。
裝載過(guò)程
當(dāng)組件第一次被掛載的時(shí)候會(huì)依次調(diào)用以下函數(shù):
constructor
componentWillMount
render
componentDidMount
這幾個(gè)函數(shù)從函數(shù)名就大概知道是什么了膘格。
constructor構(gòu)造函數(shù)峭范,非必需财松,無(wú)狀態(tài)組件可以不使用瘪贱。在存在的情況下,constructor是生命周期中被調(diào)用的第一個(gè)函數(shù)辆毡,在這里我們可以初始化state菜秦,同時(shí)綁定成員函數(shù)的this環(huán)境。
componentWillMount舶掖,非必需球昨,render函數(shù)之前被調(diào)用,與componentDidMount對(duì)稱眨攘。同時(shí)componentWillMount可以在服務(wù)端和瀏覽器端被調(diào)用主慰。
render,必須鲫售,它是React組件中最重要的函數(shù)共螺,它返回一個(gè)JSX描述的結(jié)構(gòu),之后React來(lái)操作渲染過(guò)程情竹,render函數(shù)是一個(gè)純函數(shù)藐不,完全根據(jù)this.state和this.props 來(lái)決定返回結(jié)果,因此在render函數(shù)中調(diào)用this.setState是錯(cuò)誤的秦效。
componentDidMount雏蛮,非必需,只能在瀏覽器端被調(diào)用阱州,componentDidMount函數(shù)并不是緊跟render函數(shù)后面被調(diào)用挑秉,它被調(diào)用時(shí),render函數(shù)返回的東西已經(jīng)引發(fā)來(lái)渲染苔货,組件已經(jīng)被“裝載”到了DOM樹上犀概,此時(shí)可以放心的獲取渲染出來(lái)的任何DOM,也可以通過(guò)ajax獲取數(shù)據(jù)來(lái)填充組件的內(nèi)容蒲赂。
更新過(guò)程
更新過(guò)程會(huì)調(diào)用如下生命周期函數(shù):
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
但并不是所有更新過(guò)程都會(huì)調(diào)用以上所有函數(shù)阱冶。
我們先來(lái)看看哪些情況會(huì)觸發(fā)更新過(guò)程(默認(rèn)shouldComponentUpdate返回true)
1. 父組件的render函數(shù)被調(diào)用
2. this.setState調(diào)用,但不是每調(diào)用一次就更新一次滥嘴,有時(shí)react會(huì)合并操作木蹬,并最后一次性調(diào)用render更新
3. this.forceUpdate強(qiáng)制更新
而在這些情況下會(huì)依次調(diào)用的生命周期函數(shù),如下圖所示:
componentWillReceiveProps當(dāng)父組件調(diào)用render時(shí)會(huì)觸發(fā),而不是僅僅只有props發(fā)生改變時(shí)才觸發(fā)镊叁。
shouldComponentUpdate默認(rèn)返回true尘颓,如果返回false則立即停止更新,如果恰當(dāng)使用shouldComponentUpdate來(lái)控制是否繼續(xù)更新晦譬,可以有效提高效率疤苹。
componentWillUpdate,當(dāng)shouldComponentUpdate返回true敛腌,緊接著調(diào)用componentWillUpdate卧土,之后是render,再之后是componentDidUpdate像樊。
注意:通過(guò)this.setState函數(shù)引發(fā)的更新尤莺,并不是立刻改變state的值,其實(shí)生棍,在調(diào)用shouldComponentUpdate時(shí)state的值依然是this.setState執(zhí)行之前的值颤霎。
卸載過(guò)程
componentWillUnmount,當(dāng)react組件需要從DOM樹上刪除掉之前會(huì)調(diào)用它涂滴,所以這個(gè)函數(shù)適合做一些清理工作友酱。比如,在componentDidMount中用非react的方法創(chuàng)造了一些DOM元素柔纵,如果撒手不管的話會(huì)造成內(nèi)存泄漏缔杉,那就需要在componentWillUnmount中將這些DOM卸載掉。
參考文章和書籍
http://www.reibang.com/p/4784216b8194
《深入淺出React和Redux》