深入React知識點(diǎn)整理(一)

使用React也滿三年了,從剛剛會使用到逐漸探究其底層實(shí)現(xiàn)绍移,以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼讥电。下面整理一些知識點(diǎn)蹂窖,算是React看書,使用恩敌,感悟的一些總結(jié):

  1. 函數(shù)式編程
  2. React事件系統(tǒng)
  3. 高階組件
  4. 組件性能優(yōu)化
  5. React源碼初探
  6. VirtualDOM 模型

1. 函數(shù)式編程

函數(shù)式編程是一種如何編寫程序的方法論瞬测,與之對應(yīng)的就是命令式編程

以我自己的理解纠炮,函數(shù)式編程就是以函數(shù)為中心月趟,將大段過程拆成一個(gè)個(gè)函數(shù),組合嵌套使用恢口。這個(gè)思想在JavaScript中很常見孝宗。舉個(gè)阮一峰老師的例子:

我們有一個(gè)數(shù)學(xué)表達(dá)式:

(1 + 2) * 3 - 4

將上述表達(dá)式不假思索的轉(zhuǎn)換成代碼:

const a = 1 + 2;
const b = a * 3;
const c = b - 4;

以函數(shù)式編程思想:將運(yùn)算過程定義成不同的函數(shù),如下:

const result = substract(multiply(add(1, 2), 3), 4);

是不是感覺很高端但又一臉懵逼耕肩。沒錯(cuò)因妇,函數(shù)式編程在處理大段過程中就顯得很容易理解,但是簡單邏輯中就顯得復(fù)雜猿诸,因?yàn)榉庋b起來的函數(shù)需要時(shí)間去閱讀婚被。

對上述表達(dá)式進(jìn)行變形:

 add(1,2).multiply(3).subtract(4);

是不是也很熟悉。函數(shù)式編程在JavaScript中應(yīng)用確實(shí)很普遍两芳。

目前最當(dāng)紅的Python摔寨、Ruby、Javascript怖辆,對函數(shù)式編程的支持都很強(qiáng)是复,就連老牌的面向?qū)ο蟮腏ava、面向過程的PHP竖螃,都忙不迭地加入對匿名函數(shù)的支持淑廊。越來越多的跡象表明,函數(shù)式編程已經(jīng)不再是學(xué)術(shù)界的最愛特咆,開始大踏步地在業(yè)界投入實(shí)用季惩。

也許繼"面向?qū)ο缶幊?之后录粱,"函數(shù)式編程"會成為下一個(gè)編程的主流范式(paradigm)。未來的程序員恐怕或多或少都必須懂一點(diǎn)画拾。

這里不做多介紹啥繁,有興趣可以看看:

2.React事件系統(tǒng)

React事件與DOM事件

React 基于 Virtual DOM 實(shí)現(xiàn)了一個(gè) SyntheticEvent (合成事件)層青抛,我們所定義的事件處理器會接收到一個(gè) SyntheticEvent 對象的實(shí)例旗闽,它完全符合 W3C 標(biāo)準(zhǔn),不會存在任何 IE 標(biāo)準(zhǔn)的兼容性問題蜜另。并且與原生的瀏覽器事件一樣擁有同樣的接口适室,同樣支持事件的冒泡機(jī)制,我們可以使用 stopPropagation() 和 preventDefault() 來中斷它举瑰。

所有事件都自動綁定到最外層上捣辆。如果需要訪問原生事件對象,可以使用 nativeEvent 屬性此迅。

使用React的時(shí)候都知道汽畴,React有一套自己的事件系統(tǒng),典型的特征就是元素綁定事件都要使用React提供的事件接口

// in html
<button onclick="activateLasers()">
  Activate Lasers
</button>

// in React
<button onClick={activateLasers}>
  Activate Lasers
</button>

React的合成事件實(shí)際上是做了一層事件委托(事件代理):

它并不會把事件處理函數(shù)直接綁定到真實(shí)的節(jié)點(diǎn)上邮屁,而是把所有事件綁定到結(jié)構(gòu)的最外層整袁,使用一個(gè)統(tǒng)一的事件監(jiān)聽器,這個(gè)事件監(jiān)聽器上維持了一個(gè)映射來保存所有組件內(nèi)部的事件監(jiān)聽和處理函數(shù)佑吝。當(dāng)組件掛載或卸載時(shí),只是在這個(gè)統(tǒng)一的事件監(jiān)聽器上插入或刪除一些對象绳匀;當(dāng)事件發(fā)生時(shí)芋忿,首先被這個(gè)統(tǒng)一的事件監(jiān)聽器處理,然后在映射里找到真正的事件處理函數(shù)并調(diào)用疾棵。這樣做簡化了事件處理和回收機(jī)制戈钢,效率
也有很大提升。

也就是說React使用了一個(gè)事件代理是尔,所有事件綁定都只是事件代理保存了一個(gè)映射殉了,事件發(fā)生的時(shí)候,調(diào)用處理函數(shù)拟枚,并沒有真正的使用原生事件薪铜。我們來看一個(gè)例子:

componentDidMount () {
    document.querySelector('#testEvent').addEventListener('click', (e)=>{
        console.log('dom event');
        console.log(e);
    })
}
componentDidUnMount () {
    document.querySelector('#testEvent').removeEventListener('click');
}
handleClick (e) {
    console.log('react event');
    console.log(e);
}
render () {
    return (
        <div>
              <div onClick={::this.handleClick}>Test React Event</div>
              <div id='testEvent'>Test dom Event</div>
        </div>
    );
}

這里有兩個(gè)div,使用React綁定事件和原生DOM事件恩溅,兩種事件綁定方法不同導(dǎo)致相同的效果隔箍,完全不同的原理。

使用原生DOM綁定打印的事件就是原生的脚乡,React事件打印出來的事件:
[圖片上傳失敗...(image-8bd58-1574906928855)]

可以看到是個(gè)Proxy對象蜒滩,里面有觸發(fā)事件的target和處理事件的handler,這就是React的合成事件。

另外如果在react中綁定原生事件俯艰,組件卸載的時(shí)候記得解除綁定捡遍,避免內(nèi)存泄漏。

React的合成事件還有一個(gè)優(yōu)點(diǎn)在于不需要處理瀏覽器事件兼容性竹握,方便操作稽莉。

原生事件分成三個(gè)部分:事件捕獲,目標(biāo)事件處理涩搓,事件冒泡污秆。IE9以下不支持事件捕獲,所以React沒有實(shí)現(xiàn)它昧甘,僅支持事件冒泡良拼。

有些事件React沒有實(shí)現(xiàn),window.resize事件充边。

所以庸推,請盡量避免在 React 中混用合成事件和原生 DOM 事件。因?yàn)閮烧呤遣煌氖录到y(tǒng)浇冰,阻止 React 事件冒泡的行為只能用于 React 合成事件系統(tǒng)中贬媒,且沒辦法阻止原生事件的冒泡。反之肘习,在原生事件中的阻止冒泡行為际乘,卻可以阻止 React 合成事件的傳播。

3.高階組件

高階組件是React中比較有特點(diǎn)的一類問題漂佩,高階組件(High Order Component)文章里單獨(dú)進(jìn)行了詳細(xì)介紹脖含。

這里只是補(bǔ)一張圖:組合式組件開發(fā)實(shí)踐
[圖片上傳失敗...(image-cad4f9-1574906928855)]

4.組件性能優(yōu)化

從過往的經(jīng)驗(yàn)與實(shí)踐中,我們都知道影響網(wǎng)頁性能最大的因素是瀏覽器的重繪(reflow)和重排版(repaint)投蝉。React 背后的 Virtual DOM 就是盡可能地減少瀏覽器的重繪與重排版养葵。

關(guān)于瀏覽器重繪和重排版問題,請看我之前的文章:瀏覽器渲染頁面過程與頁面優(yōu)化

這里要介紹的就是:

  1. 多使用純函數(shù):無依賴瘩缆;相同輸入相同輸出关拒;重復(fù)使用。
  2. PureComponent:本質(zhì)上講庸娱,PureComponent就是重寫了shouldComponentUpdate着绊,對nextPropsnextState與當(dāng)前state和props做淺比較,性能上優(yōu)化涌韩。
  3. Immutable:使用Immutable共享數(shù)據(jù)節(jié)點(diǎn)畔柔,節(jié)省渲染。
  4. key:列表渲染指定key臣樱,相同key不渲染靶擦;盡量不要使用index當(dāng)key腮考,最好是id。
  5. react-addons-pref:插件量化性能優(yōu)化效果玄捕。

對這塊有興趣的踩蔚,推薦幾篇文章:

5.React源碼初探

React項(xiàng)目目錄構(gòu)成如下圖:
[圖片上傳失敗...(image-abe94f-1574906928855)]

  • addons:工具方法插件:PureRenderMixinCSSTransitionGrouo枚粘、Fragment馅闽、LinkedStateMixin
  • isomorphic:包含一系列同構(gòu)方法馍迄。
  • shared:公用方法和常用方法福也。
  • test:測試方法。
  • core/tests:邊界錯(cuò)誤的測試用例攀圈。
  • renderers:React的核心代碼暴凑,包含大部分功能實(shí)現(xiàn),因此進(jìn)行單獨(dú)分析赘来。

renderers包包含內(nèi)容:
[圖片上傳失敗...(image-f3fa3f-1574906928855)]

  • dom:包含client现喳,server和shared。

    • client:包含DOM操作方法(findDOMNode犬辰,setInnerHTML嗦篱,setTextContent等)以及事件方法。這里的事件方法主要是一些非底層的實(shí)用性事件方法幌缝,
      如事件監(jiān)聽(ReactEventListener)灸促、常用事件方法(TapEventPlugin、EnterLeaveEventPlugin)以及一些合成事件(SyntheticEvents
      等)狮腿。
    • server:主要包含服務(wù)端渲染的實(shí)現(xiàn)和方法(如 ReactServerRendering腿宰、ReactServerRenderingTransaction
      等)。
    • shared:包含文本組件(ReactDOMTextComponent)缘厢、標(biāo)簽組件(ReactDOMComponent)、
      DOM 屬性操作(DOMProperty甩挫、DOMPropertyOperations)贴硫、CSS 屬性操作(CSSProperty、
      CSSPropertyOperations)等伊者。
  • shared:包含event和reconciler英遭。

    • event:包含一些更為底層的事件方法,如事件插件中心(EventPluginHub)亦渗、事件注冊
      (EventPluginRegistry)挖诸、事件傳播(EventPropagators)以及一些事件通用方法。
      React 自定義了一套通用事件的插件系統(tǒng)法精,該系統(tǒng)包含事件監(jiān)聽器多律、事件發(fā)射器痴突、事
      件插件中心、點(diǎn)擊事件狼荞、進(jìn)/出事件辽装、簡單事件、合成事件以及一些事件方法相味。
    • reconciler:稱為協(xié)調(diào)器拾积,它是最為核心的部分,包含 React 中自定義組件的實(shí)現(xiàn)
      (ReactCompositeComponent)丰涉、組件生命周期機(jī)制拓巧、setState 機(jī)制(ReactUpdates、
      ReactUpdateQueue)一死、DOM diff 算法(ReactMultiChild)等重要的特性方法肛度。

[圖片上傳失敗...(image-c78763-1574906928855)]

這里簡單介紹React目錄構(gòu)成以及每塊的功能,大致了解摘符,需要的時(shí)候找到對應(yīng)位置深入研究贤斜。

React 也能夠?qū)崿F(xiàn) Virtual DOM 的批處理更新,當(dāng)操作 Virtual DOM 時(shí), 不會馬上生成真實(shí)的DOM逛裤,而是會將一個(gè)事件循環(huán)(event loop)內(nèi)的兩次數(shù)據(jù)更新進(jìn)行合并瘩绒,這樣就使得 React 能夠在事件循環(huán)的結(jié)束之前完全不用操作真實(shí)的 DOM。

6.VirtualDOM 模型

VirtualDOM是React的一個(gè)核心带族,也是React一個(gè)著名的特點(diǎn)锁荔,之前我有篇文章對此有過簡單的介紹,以及如何簡單實(shí)現(xiàn)根據(jù)VirtualDOM渲染頁面:React學(xué)習(xí)報(bào)告蝙砌,可以做基本入門查看阳堕。

VirtualDOM與真實(shí)DOM的關(guān)系很簡單:

  • 真實(shí)DOM可以理解為是xml格式存儲DOM,VirtualDOM可以理解為json格式的存儲DOM择克。
  • 只需要存儲節(jié)點(diǎn)的關(guān)鍵信息:類型恬总,id,class肚邢,屬性壹堰,style,事件骡湖,嵌套關(guān)系等即可贱纠,按照一定的轉(zhuǎn)換規(guī)則將json轉(zhuǎn)成DOM。
  • 流程關(guān)系:jsx語法->識別jsx語法生成VirtualDOM樹->根據(jù)渲染規(guī)則生成真實(shí)DOM->HTML响蕴。

Virtual DOM中的節(jié)點(diǎn)成為ReactNode谆焊,分成ReactELement,ReactFragment浦夷,ReactText辖试。ReactElement又分成ReactComponentElemnt和ReactDOMElement辜王。

下面是 ReactNode 中不同類型節(jié)點(diǎn)所需要的基礎(chǔ)元素:

type ReactNode = ReactElement | ReactFragment | ReactText;
type ReactElement = ReactComponentElement | ReactDOMElement;
type ReactDOMElement = {
 type : string,
 props : {
 children : ReactNodeList,
 className : string,
 etc.
 },
 key : string | boolean | number | null,
 ref : string | null
};
type ReactComponentElement<TProps> = {
 type : ReactClass<TProps>,
 props : TProps,
 key : string | boolean | number | null,
 ref : string | null
};
type ReactFragment = Array<ReactNode | ReactEmpty>;
type ReactNodeList = ReactNode | ReactEmpty;
type ReactText = string | number;
type ReactEmpty = null | undefined | boolean; 

這里以DOM標(biāo)簽(ReactDOMComponent)為例,介紹VirtualDOM模型如何創(chuàng)建節(jié)點(diǎn):

屬性更新

當(dāng)執(zhí)行 mountComponent 方法時(shí)剃执,ReactDOMComponent 首先會生成標(biāo)記和標(biāo)簽誓禁,通過 this.createOpenTagMarkupAndPutListeners(transaction) 來處理 DOM 節(jié)點(diǎn)的屬性和事件。

  • 如果節(jié)點(diǎn)綁定了事件肾档,則針對當(dāng)前的節(jié)點(diǎn)添加代理摹恰,調(diào)用enqueuePutListener(this,propKey, propValue, transaction)
  • 存在樣式的話怒见,樣式合并Object.assign({}, props.style)俗慈,然后通過CSSPropertyOperations.createMarkupForStyles(propValue, this)創(chuàng)建樣式。
  • 通過DOMPropertyOperations.createMarkupForProperty(propKey, propValue)創(chuàng)建屬性遣耍。
  • 通過DOMPropertyOperations.createMarkupForID(this._domID)創(chuàng)建唯一標(biāo)識闺阱。

其實(shí),早有開發(fā)者向 React 官方提過問題舵变,建議去掉這個(gè)雞肋的屬性標(biāo)識(data-reactid)這終于在 React 15.0版本上實(shí)現(xiàn)了酣溃。據(jù)官方宣稱,去除 data-reactid 使得 React 性能有了 10% 的提升纪隙。

更新子節(jié)點(diǎn)

當(dāng)執(zhí)行 mountComponent 方法時(shí)赊豌,ReactDOMComponent 會通過 this._createContentMarkup(transaction, props, context) 來處理 DOM 節(jié)點(diǎn)的內(nèi)容。

先是刪除不需要的子節(jié)點(diǎn)和內(nèi)容绵咱。如果舊節(jié)點(diǎn)存在碘饼,而新節(jié)點(diǎn)不存在,說明當(dāng)前節(jié)點(diǎn)在更新后被刪除悲伶,此時(shí)執(zhí)行方法 this.updateChildren(null, transaction, context)艾恼;如果舊的內(nèi)容存在,而新的內(nèi)容不存在麸锉,說明當(dāng)前內(nèi)容在更新后被刪除钠绍,此時(shí)執(zhí)行方法 this.updateTextContent('')。

再是更新子節(jié)點(diǎn)和內(nèi)容花沉。如果新子節(jié)點(diǎn)存在五慈,則更新其子節(jié)點(diǎn),此時(shí)執(zhí)行方法 this.updateChildren(nextChildren,transaction, context)主穗;如果新的內(nèi)容存在,則更新內(nèi)容毙芜,此時(shí)執(zhí)行方法 this.updateTextContent('' + nextContent)忽媒。

當(dāng)卸載組件時(shí),ReactDOMComponent 會進(jìn)行一系列的操作腋粥,如卸載子節(jié)點(diǎn)晦雨、清除事件監(jiān)聽架曹、清空標(biāo)識等。
[圖片上傳失敗...(image-c05267-1574906928855)]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闹瞧,一起剝皮案震驚了整個(gè)濱河市绑雄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奥邮,老刑警劉巖万牺,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洽腺,居然都是意外死亡脚粟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門蘸朋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來核无,“玉大人,你說我怎么就攤上這事藕坯⊥拍希” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵炼彪,是天一觀的道長吐根。 經(jīng)常有香客問我,道長霹购,這世上最難降的妖魔是什么佑惠? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮齐疙,結(jié)果婚禮上膜楷,老公的妹妹穿的比我還像新娘。我一直安慰自己贞奋,他們只是感情好赌厅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著轿塔,像睡著了一般特愿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勾缭,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天揍障,我揣著相機(jī)與錄音,去河邊找鬼俩由。 笑死毒嫡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幻梯。 我是一名探鬼主播兜畸,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼努释,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咬摇?” 一聲冷哼從身側(cè)響起伐蒂,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肛鹏,沒想到半個(gè)月后逸邦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龄坪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年昭雌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片健田。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烛卧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妓局,到底是詐尸還是另有隱情总放,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布好爬,位于F島的核電站局雄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏存炮。R本人自食惡果不足惜炬搭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望穆桂。 院中可真熱鬧宫盔,春花似錦、人聲如沸享完。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽般又。三九已至彼绷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茴迁,已是汗流浹背寄悯。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堕义,地道東北人热某。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昔馋。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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

  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南糖耸,這只是我在學(xué)習(xí)過程中的一些閱讀筆記秘遏,個(gè)人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,834評論 1 18
  • react 基本概念解析 react 的組件聲明周期 react 高階組件嘉竟,context, redux 等高級...
    南航閱讀 1,069評論 0 1
  • HTML模版 之后出現(xiàn)的React代碼嵌套入模版中邦危。 1. Hello world 這段代碼將一個(gè)一級標(biāo)題插入到指...
    ryanho84閱讀 6,232評論 0 9
  • 40、React 什么是React舍扰?React 是一個(gè)用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,014評論 0 1
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,124評論 0 21