摘要:以我自己的理解,函數(shù)式編程就是以函數(shù)為中心,將大段過程拆成一個(gè)個(gè)函數(shù),組合嵌套使用。越來越多的跡象表明,函數(shù)式編程已經(jīng)不再是學(xué)術(shù)界的最愛,開始大踏步地在業(yè)界投入實(shí)用。也許繼面向?qū)ο缶幊讨?,函?shù)式編程會(huì)成為下一個(gè)編程的主流范式。
使用React也滿一年了,從剛剛會(huì)使用到逐漸探究其底層實(shí)現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識(shí)點(diǎn),算是React看書,使用,感悟的一些總結(jié):
函數(shù)式編程
React事件系統(tǒng)
高階組件
組件性能優(yōu)化
React源碼初探
VirtualDOM 模型
1. 函數(shù)式編程函數(shù)式編程是一種如何編寫程序的方法論,與之對(duì)應(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ù)式編程在處理大段過程中就顯得很容易理解,但是簡(jiǎn)單邏輯中就顯得復(fù)雜,因?yàn)榉庋b起來的函數(shù)需要時(shí)間去閱讀。
對(duì)上述表達(dá)式進(jìn)行變形:
add(1,2).multiply(3).subtract(4);
是不是也很熟悉。函數(shù)式編程在JavaScript中應(yīng)用確實(shí)很普遍。
目前最當(dāng)紅的Python、Ruby、Javascript,對(duì)函數(shù)式編程的支持都很強(qiáng),就連老牌的面向?qū)ο蟮腏ava、面向過程的PHP,都忙不迭地加入對(duì)匿名函數(shù)的支持。越來越多的跡象表明,函數(shù)式編程已經(jīng)不再是學(xué)術(shù)界的最愛,開始大踏步地在業(yè)界投入實(shí)用。也許繼"面向?qū)ο缶幊?之后,"函數(shù)式編程"會(huì)成為下一個(gè)編程的主流范式(paradigm)。未來的程序員恐怕或多或少都必須懂一點(diǎn)。
這里不做多介紹,有興趣可以看看:
函數(shù)式編程初探
什么是函數(shù)式編程思維?
函數(shù)式編程
2.React事件系統(tǒng) React事件與DOM事件React 基于 Virtual DOM 實(shí)現(xiàn)了一個(gè) SyntheticEvent (合成事件)層,我們所定義的事件處理器會(huì)接收到一個(gè) SyntheticEvent 對(duì)象的實(shí)例,它完全符合 W3C 標(biāo)準(zhǔn),不會(huì)存在任何 IE 標(biāo)準(zhǔn)的兼容性問題。并且與原生的瀏覽器事件一樣擁有同樣的接口,同樣支持事件的冒泡機(jī)制,我們可以使用 stopPropagation() 和 preventDefault() 來中斷它。所有事件都自動(dòng)綁定到最外層上。如果需要訪問原生事件對(duì)象,可以使用 nativeEvent 屬性。
使用React的時(shí)候都知道,React有一套自己的事件系統(tǒng),典型的特征就是元素綁定事件都要使用React提供的事件接口:
// in html // in React
React的合成事件實(shí)際上是做了一層事件委托(事件代理):
它并不會(huì)把事件處理函數(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)聽器上插入或刪除一些對(duì)象;當(dāng)事件發(fā)生時(shí),首先被這個(gè)統(tǒng)一的事件監(jiān)聽器處理,然后在映射里找到真正的事件處理函數(shù)并調(diào)用。這樣做簡(jiǎn)化了事件處理和回收機(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 (); }Test React EventTest dom Event
這里有兩個(gè)div,使用React綁定事件和原生DOM事件,兩種事件綁定方法不同導(dǎo)致相同的效果,完全不同的原理。
使用原生DOM綁定打印的事件就是原生的,React事件打印出來的事件:
可以看到是個(gè)Proxy對(duì)象,里面有觸發(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事件。
所以,請(qǐng)盡量避免在 React 中混用合成事件和原生 DOM 事件。因?yàn)閮烧呤遣煌氖录到y(tǒng),阻止 React 事件冒泡的行為只能用于 React 合成事件系統(tǒng)中,且沒辦法阻止原生事件的冒泡。反之,在原生事件中的阻止冒泡行為,卻可以阻止 React 合成事件的傳播。
3.高階組件高階組件是React中比較有特點(diǎn)的一類問題,高階組件(High Order Component)文章里多帶帶進(jìn)行了詳細(xì)介紹。
這里只是補(bǔ)一張圖:組合式組件開發(fā)實(shí)踐
從過往的經(jīng)驗(yàn)與實(shí)踐中,我們都知道影響網(wǎng)頁(yè)性能最大的因素是瀏覽器的重繪(reflow)和重排版(repaint)。React 背后的 Virtual DOM 就是盡可能地減少瀏覽器的重繪與重排版。
關(guān)于瀏覽器重繪和重排版問題,請(qǐng)看我之前的文章:瀏覽器渲染頁(yè)面過程與頁(yè)面優(yōu)化
這里要介紹的就是:
多使用純函數(shù):無依賴;相同輸入相同輸出;重復(fù)使用。
PureComponent:本質(zhì)上講,PureComponent就是重寫了shouldComponentUpdate,對(duì)nextProps和nextState與當(dāng)前state和props做淺比較,性能上優(yōu)化。
Immutable:使用Immutable共享數(shù)據(jù)節(jié)點(diǎn),節(jié)省渲染。
key:列表渲染指定key,相同key不渲染;盡量不要使用index當(dāng)key,最好是id。
react-addons-pref:插件量化性能優(yōu)化效果。
對(duì)這塊有興趣的,推薦幾篇文章:
React組件性能調(diào)優(yōu)
React性能優(yōu)化總結(jié)
高性能 React 組件
5.React源碼初探React項(xiàng)目目錄構(gòu)成如下圖:
addons:工具方法插件:PureRenderMixin、CSSTransitionGrouo、Fragment、LinkedStateMixin。
isomorphic:包含一系列同構(gòu)方法。
shared:公用方法和常用方法。
test:測(cè)試方法。
core/tests:邊界錯(cuò)誤的測(cè)試用例。
renderers:React的核心代碼,包含大部分功能實(shí)現(xiàn),因此進(jìn)行多帶帶分析。
renderers包包含內(nèi)容:
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)、事件注冊(cè)
(EventPluginRegistry)、事件傳播(EventPropagators)以及一些事件通用方法。
React 自定義了一套通用事件的插件系統(tǒng),該系統(tǒng)包含事件監(jiān)聽器、事件發(fā)射器、事
件插件中心、點(diǎn)擊事件、進(jìn)/出事件、簡(jiǎn)單事件、合成事件以及一些事件方法。
reconciler:稱為協(xié)調(diào)器,它是最為核心的部分,包含 React 中自定義組件的實(shí)現(xiàn)
(ReactCompositeComponent)、組件生命周期機(jī)制、setState 機(jī)制(ReactUpdates、
ReactUpdateQueue)、DOM diff 算法(ReactMultiChild)等重要的特性方法。
這里簡(jiǎn)單介紹React目錄構(gòu)成以及每塊的功能,大致了解,需要的時(shí)候找到對(duì)應(yīng)位置深入研究。
React 也能夠?qū)崿F(xiàn) Virtual DOM 的批處理更新,當(dāng)操作 Virtual DOM 時(shí), 不會(huì)馬上生成真實(shí)的DOM,而是會(huì)將一個(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),之前我有篇文章對(duì)此有過簡(jiǎn)單的介紹,以及如何簡(jiǎn)單實(shí)現(xiàn)根據(jù)VirtualDOM渲染頁(yè)面:React學(xué)習(xí)報(bào)告,可以做基本入門查看。
VirtualDOM與真實(shí)DOM的關(guān)系很簡(jiǎn)單:
真實(shí)DOM可以理解為是xml格式存儲(chǔ)DOM,VirtualDOM可以理解為json格式的存儲(chǔ)DOM。
只需要存儲(chǔ)節(jié)點(diǎn)的關(guān)鍵信息:類型,id,class,屬性,style,事件,嵌套關(guān)系等即可,按照一定的轉(zhuǎn)換規(guī)則將json轉(zhuǎn)成DOM。
流程關(guān)系:jsx語(yǔ)法->識(shí)別jsx語(yǔ)法生成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= { type : ReactClass , props : TProps, key : string | boolean | number | null, ref : string | null }; type ReactFragment = Array ; 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 首先會(huì)生成標(biāo)記和標(biāo)簽,通過 this.createOpenTagMarkupAndPutListeners(transaction) 來處理 DOM 節(jié)點(diǎn)的屬性和事件。
如果節(jié)點(diǎn)綁定了事件,則針對(duì)當(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í)。
其實(shí),早有開發(fā)者向 React 官方提過問題,建議去掉這個(gè)雞肋的屬性標(biāo)識(shí)(data-reactid)這終于在 React 15.0版本上實(shí)現(xiàn)了。據(jù)官方宣稱,去除 data-reactid 使得 React 性能有了 10% 的提升。更新子節(jié)點(diǎn)
當(dāng)執(zhí)行 mountComponent 方法時(shí),ReactDOMComponent 會(huì)通過 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 會(huì)進(jìn)行一系列的操作,如卸載子節(jié)點(diǎn)、清除事件監(jiān)聽、清空標(biāo)識(shí)等。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92140.html
摘要:承接上文,深入知識(shí)點(diǎn)整理一使用也滿一年了,從剛剛會(huì)使用到逐漸探究其底層實(shí)現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。有限狀態(tài)機(jī),表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的模型。 承接上文,深入React知識(shí)點(diǎn)整理(一)使用React也滿一年了,從剛剛會(huì)使用到逐漸探究其底層實(shí)現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識(shí)點(diǎn),...
showImg(https://segmentfault.com/img/remote/1460000018716142?w=200&h=200); showImg(https://segmentfault.com/img/remote/1460000018716143);showImg(https://segmentfault.com/img/remote/1460000010953710);...
摘要:因?yàn)楣ぷ髦幸恢痹谑褂?,也一直以來想總結(jié)一下自己關(guān)于的一些知識(shí)經(jīng)驗(yàn)。于是把一些想法慢慢整理書寫下來,做成一本開源免費(fèi)專業(yè)簡(jiǎn)單的入門級(jí)別的小書,提供給社區(qū)。本書的后續(xù)可能會(huì)做成視頻版本,敬請(qǐng)期待。本作品采用署名禁止演繹國(guó)際許可協(xié)議進(jìn)行許可 React.js 小書 本文作者:胡子大哈本文原文:React.js 小書 轉(zhuǎn)載請(qǐng)注明出處,保留原文鏈接以及作者信息 在線閱讀:http://huzi...
摘要:詳解十大常用設(shè)計(jì)模式力薦深度好文深入理解大設(shè)計(jì)模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實(shí)現(xiàn)方式 延遲加載也稱為惰性加載,即在長(zhǎng)網(wǎng)頁(yè)中延遲加載圖像。用戶滾動(dòng)到它們之前,視口外的圖像不會(huì)加載。本文詳細(xì)介紹了三種延遲加載的實(shí)現(xiàn)方式。 詳解 Javascript十大常用設(shè)計(jì)模式 力薦~ ...
閱讀 3161·2021-11-22 12:01
閱讀 3781·2021-08-30 09:46
閱讀 795·2019-08-30 13:48
閱讀 3229·2019-08-29 16:43
閱讀 1674·2019-08-29 16:33
閱讀 1864·2019-08-29 13:44
閱讀 1427·2019-08-26 13:45
閱讀 2242·2019-08-26 11:44