摘要:以我自己的理解,函數式編程就是以函數為中心,將大段過程拆成一個個函數,組合嵌套使用。越來越多的跡象表明,函數式編程已經不再是學術界的最愛,開始大踏步地在業界投入實用。也許繼面向對象編程之后,函數式編程會成為下一個編程的主流范式。
使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,算是React看書,使用,感悟的一些總結:
函數式編程
React事件系統
高階組件
組件性能優化
React源碼初探
VirtualDOM 模型
1. 函數式編程函數式編程是一種如何編寫程序的方法論,與之對應的就是命令式編程。
以我自己的理解,函數式編程就是以函數為中心,將大段過程拆成一個個函數,組合嵌套使用。這個思想在JavaScript中很常見。舉個阮一峰老師的例子:
我們有一個數學表達式:
(1 + 2) * 3 - 4
將上述表達式不假思索的轉換成代碼:
const a = 1 + 2; const b = a * 3; const c = b - 4;
以函數式編程思想:將運算過程定義成不同的函數,如下:
const result = substract(multiply(add(1, 2), 3), 4);
是不是感覺很高端但又一臉懵逼。沒錯,函數式編程在處理大段過程中就顯得很容易理解,但是簡單邏輯中就顯得復雜,因為封裝起來的函數需要時間去閱讀。
對上述表達式進行變形:
add(1,2).multiply(3).subtract(4);
是不是也很熟悉。函數式編程在JavaScript中應用確實很普遍。
目前最當紅的Python、Ruby、Javascript,對函數式編程的支持都很強,就連老牌的面向對象的Java、面向過程的PHP,都忙不迭地加入對匿名函數的支持。越來越多的跡象表明,函數式編程已經不再是學術界的最愛,開始大踏步地在業界投入實用。也許繼"面向對象編程"之后,"函數式編程"會成為下一個編程的主流范式(paradigm)。未來的程序員恐怕或多或少都必須懂一點。
這里不做多介紹,有興趣可以看看:
函數式編程初探
什么是函數式編程思維?
函數式編程
2.React事件系統 React事件與DOM事件React 基于 Virtual DOM 實現了一個 SyntheticEvent (合成事件)層,我們所定義的事件處理器會接收到一個 SyntheticEvent 對象的實例,它完全符合 W3C 標準,不會存在任何 IE 標準的兼容性問題。并且與原生的瀏覽器事件一樣擁有同樣的接口,同樣支持事件的冒泡機制,我們可以使用 stopPropagation() 和 preventDefault() 來中斷它。所有事件都自動綁定到最外層上。如果需要訪問原生事件對象,可以使用 nativeEvent 屬性。
使用React的時候都知道,React有一套自己的事件系統,典型的特征就是元素綁定事件都要使用React提供的事件接口:
// in html // in React
React的合成事件實際上是做了一層事件委托(事件代理):
它并不會把事件處理函數直接綁定到真實的節點上,而是把所有事件綁定到結構的最外層,使用一個統一的事件監聽器,這個事件監聽器上維持了一個映射來保存所有組件內部的事件監聽和處理函數。當組件掛載或卸載時,只是在這個統一的事件監聽器上插入或刪除一些對象;當事件發生時,首先被這個統一的事件監聽器處理,然后在映射里找到真正的事件處理函數并調用。這樣做簡化了事件處理和回收機制,效率
也有很大提升。
也就是說React使用了一個事件代理,所有事件綁定都只是事件代理保存了一個映射,事件發生的時候,調用處理函數,并沒有真正的使用原生事件。我們來看一個例子:
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
這里有兩個div,使用React綁定事件和原生DOM事件,兩種事件綁定方法不同導致相同的效果,完全不同的原理。
使用原生DOM綁定打印的事件就是原生的,React事件打印出來的事件:
可以看到是個Proxy對象,里面有觸發事件的target和處理事件的handler,這就是React的合成事件。
另外如果在react中綁定原生事件,組件卸載的時候記得解除綁定,避免內存泄漏。
React的合成事件還有一個優點在于不需要處理瀏覽器事件兼容性,方便操作。
原生事件分成三個部分:事件捕獲,目標事件處理,事件冒泡。IE9以下不支持事件捕獲,所以React沒有實現它,僅支持事件冒泡。有些事件React沒有實現,window.resize事件。
所以,請盡量避免在 React 中混用合成事件和原生 DOM 事件。因為兩者是不同的事件系統,阻止 React 事件冒泡的行為只能用于 React 合成事件系統中,且沒辦法阻止原生事件的冒泡。反之,在原生事件中的阻止冒泡行為,卻可以阻止 React 合成事件的傳播。
3.高階組件高階組件是React中比較有特點的一類問題,高階組件(High Order Component)文章里多帶帶進行了詳細介紹。
這里只是補一張圖:組合式組件開發實踐
從過往的經驗與實踐中,我們都知道影響網頁性能最大的因素是瀏覽器的重繪(reflow)和重排版(repaint)。React 背后的 Virtual DOM 就是盡可能地減少瀏覽器的重繪與重排版。
關于瀏覽器重繪和重排版問題,請看我之前的文章:瀏覽器渲染頁面過程與頁面優化
這里要介紹的就是:
多使用純函數:無依賴;相同輸入相同輸出;重復使用。
PureComponent:本質上講,PureComponent就是重寫了shouldComponentUpdate,對nextProps和nextState與當前state和props做淺比較,性能上優化。
Immutable:使用Immutable共享數據節點,節省渲染。
key:列表渲染指定key,相同key不渲染;盡量不要使用index當key,最好是id。
react-addons-pref:插件量化性能優化效果。
對這塊有興趣的,推薦幾篇文章:
React組件性能調優
React性能優化總結
高性能 React 組件
5.React源碼初探React項目目錄構成如下圖:
addons:工具方法插件:PureRenderMixin、CSSTransitionGrouo、Fragment、LinkedStateMixin。
isomorphic:包含一系列同構方法。
shared:公用方法和常用方法。
test:測試方法。
core/tests:邊界錯誤的測試用例。
renderers:React的核心代碼,包含大部分功能實現,因此進行多帶帶分析。
renderers包包含內容:
dom:包含client,server和shared。
client:包含DOM操作方法(findDOMNode,setInnerHTML,setTextContent等)以及事件方法。這里的事件方法主要是一些非底層的實用性事件方法,
如事件監聽(ReactEventListener)、常用事件方法(TapEventPlugin、EnterLeaveEventPlugin)以及一些合成事件(SyntheticEvents
等)。
server:主要包含服務端渲染的實現和方法(如 ReactServerRendering、ReactServerRenderingTransaction
等)。
shared:包含文本組件(ReactDOMTextComponent)、標簽組件(ReactDOMComponent)、
DOM 屬性操作(DOMProperty、DOMPropertyOperations)、CSS 屬性操作(CSSProperty、
CSSPropertyOperations)等。
shared:包含event和reconciler。
event:包含一些更為底層的事件方法,如事件插件中心(EventPluginHub)、事件注冊
(EventPluginRegistry)、事件傳播(EventPropagators)以及一些事件通用方法。
React 自定義了一套通用事件的插件系統,該系統包含事件監聽器、事件發射器、事
件插件中心、點擊事件、進/出事件、簡單事件、合成事件以及一些事件方法。
reconciler:稱為協調器,它是最為核心的部分,包含 React 中自定義組件的實現
(ReactCompositeComponent)、組件生命周期機制、setState 機制(ReactUpdates、
ReactUpdateQueue)、DOM diff 算法(ReactMultiChild)等重要的特性方法。
這里簡單介紹React目錄構成以及每塊的功能,大致了解,需要的時候找到對應位置深入研究。
React 也能夠實現 Virtual DOM 的批處理更新,當操作 Virtual DOM 時, 不會馬上生成真實的DOM,而是會將一個事件循環(event loop)內的兩次數據更新進行合并,這樣就使得 React 能夠在事件循環的結束之前完全不用操作真實的 DOM。6.VirtualDOM 模型
VirtualDOM是React的一個核心,也是React一個著名的特點,之前我有篇文章對此有過簡單的介紹,以及如何簡單實現根據VirtualDOM渲染頁面:React學習報告,可以做基本入門查看。
VirtualDOM與真實DOM的關系很簡單:
真實DOM可以理解為是xml格式存儲DOM,VirtualDOM可以理解為json格式的存儲DOM。
只需要存儲節點的關鍵信息:類型,id,class,屬性,style,事件,嵌套關系等即可,按照一定的轉換規則將json轉成DOM。
流程關系:jsx語法->識別jsx語法生成VirtualDOM樹->根據渲染規則生成真實DOM->HTML。
Virtual DOM中的節點成為ReactNode,分成ReactELement,ReactFragment,ReactText。ReactElement又分成ReactComponentElemnt和ReactDOMElement。
下面是 ReactNode 中不同類型節點所需要的基礎元素:
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標簽(ReactDOMComponent)為例,介紹VirtualDOM模型如何創建節點:
屬性更新當執行 mountComponent 方法時,ReactDOMComponent 首先會生成標記和標簽,通過 this.createOpenTagMarkupAndPutListeners(transaction) 來處理 DOM 節點的屬性和事件。
如果節點綁定了事件,則針對當前的節點添加代理,調用enqueuePutListener(this,propKey, propValue, transaction)。
存在樣式的話,樣式合并Object.assign({}, props.style),然后通過CSSPropertyOperations.createMarkupForStyles(propValue, this)創建樣式。
通過DOMPropertyOperations.createMarkupForProperty(propKey, propValue)創建屬性。
通過DOMPropertyOperations.createMarkupForID(this._domID)創建唯一標識。
其實,早有開發者向 React 官方提過問題,建議去掉這個雞肋的屬性標識(data-reactid)這終于在 React 15.0版本上實現了。據官方宣稱,去除 data-reactid 使得 React 性能有了 10% 的提升。更新子節點
當執行 mountComponent 方法時,ReactDOMComponent 會通過 this._createContentMarkup(transaction, props, context) 來處理 DOM 節點的內容。
先是刪除不需要的子節點和內容。如果舊節點存在,而新節點不存在,說明當前節點在更新后被刪除,此時執行方法 this.updateChildren(null, transaction, context);如果舊的內容存在,而新的內容不存在,說明當前內容在更新后被刪除,此時執行方法 this.updateTextContent("")。
再是更新子節點和內容。如果新子節點存在,則更新其子節點,此時執行方法 this.updateChildren(nextChildren,transaction, context);如果新的內容存在,則更新內容,此時執行方法 this.updateTextContent("" + nextContent)。
當卸載組件時,ReactDOMComponent 會進行一系列的操作,如卸載子節點、清除事件監聽、清空標識等。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92140.html
摘要:承接上文,深入知識點整理一使用也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。有限狀態機,表示有限個狀態以及在這些狀態之間的轉移和動作等行為的模型。 承接上文,深入React知識點整理(一)使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,...
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);...
摘要:因為工作中一直在使用,也一直以來想總結一下自己關于的一些知識經驗。于是把一些想法慢慢整理書寫下來,做成一本開源免費專業簡單的入門級別的小書,提供給社區。本書的后續可能會做成視頻版本,敬請期待。本作品采用署名禁止演繹國際許可協議進行許可 React.js 小書 本文作者:胡子大哈本文原文:React.js 小書 轉載請注明出處,保留原文鏈接以及作者信息 在線閱讀:http://huzi...
閱讀 3145·2021-11-22 12:01
閱讀 3767·2021-08-30 09:46
閱讀 783·2019-08-30 13:48
閱讀 3209·2019-08-29 16:43
閱讀 1657·2019-08-29 16:33
閱讀 1847·2019-08-29 13:44
閱讀 1410·2019-08-26 13:45
閱讀 2228·2019-08-26 11:44