對(duì)React一些原理的理解

原文地址在我的博客, 轉(zhuǎn)載請(qǐng)注明出處,謝謝!

前言

隨著項(xiàng)目開(kāi)發(fā)的深入拉鹃,不可避免了遇到了一些問(wèn)題。剛開(kāi)始出現(xiàn)問(wèn)題時(shí)很懵鲫忍,不知道該怎么解決膏燕,原因就是對(duì)React的原理理解的不夠透徹,不知道問(wèn)題出在哪悟民。在解決問(wèn)題的過(guò)程中坝辫,也逐漸深入了解了React的一些原理,這篇文章就來(lái)分享一下我對(duì)React一些原理的理解射亏。

注意 這篇文章并不是教程近忙,只是我對(duì)React原理的一些個(gè)人理解,歡迎與我一起討論鸦泳。文章不對(duì)的地方银锻,還請(qǐng)讀者費(fèi)心指出-

概述

本文是《使用React技術(shù)棧的一些收獲》系列文章的第二篇(第一篇在這里,介紹如何開(kāi)始構(gòu)建React大型項(xiàng)目)做鹰,簡(jiǎn)單介紹了React一些原理击纬,包括React合成事件系統(tǒng)、組件的生命周期以及setState()钾麸。

React合成事件系統(tǒng)

React快速的原因之一就是React很少直接操作DOM更振,瀏覽器事件也是一樣炕桨。原因是太多的瀏覽器事件會(huì)占用很大內(nèi)存。

React為此自己實(shí)現(xiàn)了一套合成系統(tǒng)肯腕,在DOM事件體系基礎(chǔ)上做了很大改進(jìn)献宫,減少了內(nèi)存消耗,簡(jiǎn)化了事件邏輯实撒,最大化解決瀏覽器兼容問(wèn)題姊途。

其基本原理就是,所有在JSX聲明的事件都會(huì)被委托在頂層document節(jié)點(diǎn)上知态,并根據(jù)事件名和組件名存儲(chǔ)回調(diào)函數(shù)(listenerBank)捷兰。每次當(dāng)某個(gè)組件觸發(fā)事件時(shí),在document節(jié)點(diǎn)上綁定的監(jiān)聽(tīng)函數(shù)(dispatchEvent)就會(huì)找到這個(gè)組件和它的所有父組件(ancestors)负敏,對(duì)每個(gè)組件創(chuàng)建對(duì)應(yīng)React合成事件(SyntheticEvent)并批處理(runEventQueueInBatch(events))贡茅,從而根據(jù)事件名和組件名調(diào)用(invokeGuardedCallback)回調(diào)函數(shù)。

因此其做,如果你采用下面這種寫(xiě)法顶考,并且這樣的P標(biāo)簽有很多個(gè):

listView = list.map((item,index) => {
    return (
        <p onClick={this.handleClick} key={item.id}>{item.text}</p>
    )
})

That's OK,React幫你實(shí)現(xiàn)了事件委托妖泄。我之前因?yàn)椴涣私釸eact合成事件系統(tǒng)驹沿,還顯示的使用了事件委托,現(xiàn)在看來(lái)是多此一舉的蹈胡。

由于React合成事件系統(tǒng)模擬事件冒泡的方法是構(gòu)建一個(gè)自己及父組件隊(duì)列甚负,因此也帶來(lái)一個(gè)問(wèn)題,合成事件不能阻止原生事件审残,原生事件可以阻止合成事件。用 event.stopPropagation() 并不能停止事件傳播斑举,應(yīng)該使用 event.preventDefault()搅轿。

如果你想詳細(xì)了解React合成事件系統(tǒng),移步http://blog.csdn.net/u013510838/article/details/61224760

組件的生命周期(以父子組件為例)

為了搞清楚組件生命周期富玷,構(gòu)造一個(gè)父組件包含子組件并且重寫(xiě)各生命周期函數(shù)的場(chǎng)景:

class Child extends React.Component {
  constructor() {
    super()
    console.log('Child was created!')
  }
  componentWillMount(){
    console.log('Child componentWillMount!')
  }
  componentDidMount(){
    console.log('Child componentDidMount!')
  }
  componentWillReceiveProps(nextProps){
    console.log('Child componentWillReceiveProps:'+nextProps.data )
  }
  shouldComponentUpdate(nextProps, nextState){
    console.log('Child shouldComponentUpdate:'+ nextProps.data)
    return true
  }
  componentWillUpdate(nextProps, nextState){
    console.log('Child componentWillUpdate:'+ nextProps.data)
  }
  componentDidUpdate(){
    console.log('Child componentDidUpdate')
  }
  render() {
    console.log('render Child!')
    return (      
      <h1>Child recieve props: {this.props.data}</h1>      
    );
  }
}

class Father extends React.Component {
  // ... 前面跟子組件一樣
  handleChangeState(){
    this.setState({randomData: Math.floor(Math.random()*50)})
  }
  render() {
    console.log('render Father!')
    return (
      <div>
        <Child data={this.state.randomData} />
        <h1>Father State: { this.state.randomData}</h1>      
        <button onClick={this.handleChangeState}>切換狀態(tài)</button>
      </div>
    );
  }
}

React.render(
  <Father />,
  document.getElementById('root')
);

結(jié)果如下:
剛開(kāi)始

Alt text

調(diào)用父組件的setState后:
Alt text

在Jsbin上試試看
有一張圖能說(shuō)明這之間的流程(圖片來(lái)源):
Alt text

setState并不奇怪

有一個(gè)能反映問(wèn)題的場(chǎng)景:

...
state = {
    count: 0
}
componentDidMount() {
  this.setState({count: this.state.count + 1})
  this.setState({count: this.state.count + 1})
  this.setState({count: this.state.count + 1})
}
...

看起來(lái)state.count被增加了三次璧坟,但結(jié)果是增加了一次。這并不奇怪:

React快的原因之一就是赎懦,在執(zhí)行this.setState()時(shí)雀鹃,React沒(méi)有忙著立即更新state,只是把新的state存到一個(gè)隊(duì)列(batchUpdate)中励两。上面三次執(zhí)行setState只是對(duì)傳進(jìn)去的對(duì)象進(jìn)行了合并,然后再統(tǒng)一處理(批處理)黎茎,觸發(fā)重新渲染過(guò)程,因此只重新渲染一次当悔,結(jié)果只增加了一次傅瞻。這樣做是非常明智的踢代,因?yàn)樵谝粋€(gè)函數(shù)里調(diào)用多個(gè)setState是常見(jiàn)的,如果每一次調(diào)用setState都要引發(fā)重新渲染嗅骄,顯然不是最佳實(shí)踐胳挎。React官方文檔里也說(shuō)了:

Think of setState() as a request rather than an immediate command to update the component.

setState() 看作是重新render的一次請(qǐng)求而不是立刻更新組件的指令。

那么調(diào)用this.setState()后什么時(shí)候this.state才會(huì)更新溺森?
答案是即將要執(zhí)行下一次的render函數(shù)時(shí)慕爬。

這之間發(fā)生了什么?
setState調(diào)用后屏积,React會(huì)執(zhí)行一個(gè)事務(wù)(Transaction)医窿,在這個(gè)事務(wù)中,React將新state放進(jìn)一個(gè)隊(duì)列中肾请,當(dāng)事務(wù)完成后留搔,React就會(huì)刷新隊(duì)列,然后啟動(dòng)另一個(gè)事務(wù)铛铁,這個(gè)事務(wù)包括執(zhí)行 shouldComponentUpdate 方法來(lái)判斷是否重新渲染隔显,如果是,React就會(huì)進(jìn)行state合并(state merge),生成新的state和props饵逐;如果不是括眠,React仍然會(huì)更新this.state,只不過(guò)不會(huì)再render了倍权。

開(kāi)發(fā)人員對(duì)setState感到奇怪的原因可能就是按照上述寫(xiě)法并不能產(chǎn)生預(yù)期效果掷豺,但幸運(yùn)的是我們改動(dòng)一下就可以實(shí)現(xiàn)上述累加效果:
這歸功于setState可以接受函數(shù)作為參數(shù):

setState(updater, [callback])

...
state = {
    score: 0
}
componentDidMount() {
    this.setState( (prevState) => ({score : prevState.score + 1}) )
    this.setState( (prevState) => ({score : prevState.score + 1}) )
    this.setState( (prevState) => ({score : prevState.score + 1}) )
  }
}

這個(gè)updater可以為函數(shù),該函數(shù)接受該組件前一刻的 state 以及當(dāng)前的 props 作為參數(shù)薄声,計(jì)算和返回下一刻的 state当船。

你會(huì)發(fā)現(xiàn)達(dá)到增加三次的目的了: 在Jsbin上試試看

這是因?yàn)镽eact會(huì)把setState里傳進(jìn)去的函數(shù)放在一個(gè)任務(wù)隊(duì)列里,React 會(huì)依次調(diào)用隊(duì)列中的函數(shù)默辨,傳遞給它們前一刻的 state德频。

另外,不知道你在jsbin上的代碼上注意到?jīng)]有缩幸,調(diào)用setStateconsole.log(this.state.score)輸出仍然為0壹置,也就是this.state并未改變,并且只render了一次表谊。

總結(jié)

學(xué)習(xí)一個(gè)框架或者工具钞护,我覺(jué)得應(yīng)該了解以下幾點(diǎn):

  1. 它是什么?能做什么爆办?
  2. 它存在的理由是什么难咕?解決了什么樣的問(wèn)題、滿(mǎn)足了什么樣的需求?
  3. 它的適用場(chǎng)景是什么步藕??jī)?yōu)缺點(diǎn)是什么惦界?
  4. 它怎么用?最佳實(shí)踐是什么咙冗?
  5. 它的原理是什么沾歪?
  6. ...

通過(guò)對(duì)React一些原理的簡(jiǎn)單了解,就懂得了React為什么這么快速的原因之一雾消,也會(huì)在問(wèn)題出現(xiàn)時(shí)知道錯(cuò)在什么地方灾搏,知道合理的解決方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末立润,一起剝皮案震驚了整個(gè)濱河市狂窑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桑腮,老刑警劉巖泉哈,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異破讨,居然都是意外死亡丛晦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)提陶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)烫沙,“玉大人,你說(shuō)我怎么就攤上這事隙笆⌒啃睿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵撑柔,是天一觀的道長(zhǎng)瘸爽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铅忿,這世上最難降的妖魔是什么蝶糯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮辆沦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识虚。我一直安慰自己肢扯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布担锤。 她就那樣靜靜地躺著蔚晨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铭腕,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天银择,我揣著相機(jī)與錄音,去河邊找鬼累舷。 笑死浩考,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的被盈。 我是一名探鬼主播析孽,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼只怎!你這毒婦竟也來(lái)了袜瞬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤身堡,失蹤者是張志新(化名)和其女友劉穎邓尤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贴谎,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汞扎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赴精。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佩捞。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蕾哟,靈堂內(nèi)的尸體忽然破棺而出一忱,到底是詐尸還是另有隱情,我是刑警寧澤谭确,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布帘营,位于F島的核電站,受9級(jí)特大地震影響逐哈,放射性物質(zhì)發(fā)生泄漏芬迄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一昂秃、第九天 我趴在偏房一處隱蔽的房頂上張望禀梳。 院中可真熱鬧,春花似錦肠骆、人聲如沸算途。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嘴瓤。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廓脆,已是汗流浹背筛谚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留停忿,地道東北人驾讲。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞎嬉,于是被迫代替她去往敵國(guó)和親蝎毡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 做React需要會(huì)什么? react的功能其實(shí)很單一便监,主要負(fù)責(zé)渲染的功能扎谎,現(xiàn)有的框架,比如angular是一個(gè)大而...
    蒼都閱讀 14,745評(píng)論 1 139
  • 原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南烧董,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記毁靶,個(gè)人覺(jué)得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,813評(píng)論 1 18
  • MacBook Pro擁有非常強(qiáng)大的配置逊移,但是有些人想強(qiáng)上加強(qiáng)预吆,他們想用Linux系統(tǒng)。 不管您是想要更開(kāi)放和個(gè)性...
    S_Lance閱讀 39,534評(píng)論 9 20
  • The best is not always the best, but the best. 如果每一個(gè)城市也有星...
    S_BEER艾斯比爾閱讀 246評(píng)論 0 1
  • 前不久接觸的湖北經(jīng)視xx欄目組胳泉,成為了一名幕后實(shí)習(xí)編輯拐叉,就是打打文字稿。本來(lái)是個(gè)很輕松的活扇商,可是我心里一點(diǎn)也不輕松...
    Fncy曼閱讀 700評(píng)論 2 1