本文主要談本身在react學習的過程當中總結出來的一些經驗和資源,內容邏輯參考了「深刻react技術棧」一書以及網上的諸多資源,但也並不是徹底照抄,代碼基本都是本身實踐,主要爲平時我的學習作一個總結和參考。css
本文的關鍵內容:樣式處理與css模塊化、組件間通訊(非flux架構)、組件抽象、組件性能優化以及React 動畫五種內容。html
在react出現以前,咱們寫樣式通常是將css分離的,而且用less/sass預處理器,我我的在用backbone等MV*框架的時候就習慣用less而且用nodejs配置一個模塊用來編譯less。前端
但這樣寫會有一些問題:node
命名衝突是一個很常見的問題,所以,咱們要制定出一套本身的完整命名規範來,而且要防止和項目中引入的庫出現衝突。react
充分利用優先級是一個比較好的實踐,可是這樣寫出的less代碼有點像回調函數塔,雖然我本人並不以爲這有什麼很差甚至還比較享受這種編程,但這的確不利於充分壓縮css代碼。git
因而咱們引入css modules。github
簡單的說,若是咱們配置了css modules的話,那麼你在css中寫的類名和你在組件中寫的class = ...
都會被從新編譯成一個哈希字符串,這樣咱們就不用考慮命名衝突的問題了,另外也能夠比較自由的在local和global的css變量之間切換(實際上,這樣的css變量默認都是local的,若是須要global,咱們須要:global
前綴,這樣的話css變量就不會被轉化成特殊的哈希值了)web
須要注意的是寫法問題,這個時候咱們就不能在jsx中僅僅用className了,css module實際上限制了咱們必需要用className={style.title}
這樣的寫法,實際上我在嘗試的時候由於這個地方的bug調試了好久,而這也在某一種程度上給利用css module進行重構代碼帶來了一些困難。算法
關於css modules的入門介紹,沒錯,阮一峯老師寫了一份:http://www.ruanyifeng.com/blo... spring
另外,有的同窗認爲css modules並不夠優雅,實際上上文的寫法限定的問題就是一個麻煩事,因此咱們能夠用react-css-modules庫,這個庫解決了css modules的一些不是很好的問題,由於上手並不難,這裏不詳細介紹了(能夠參考這裏以及「深刻react技術棧」73頁)
接下來咱們總結一下react組件間通訊的幾種方式,雖然如今有了redux等最佳實踐,可是不少時候咱們仍是須要原生可用的組件通訊機制。
很是常見,經過props機制傳遞便可。
利用回調函數,回調函數自己定義在父組件中,經過props方式傳遞給子組件,在子組件中調用回調函數。
利用自定義事件機制,這種方法更通用方便,而且能夠簡化API,關於自定義事件機制的詳細使用方法咱們在接下來展開。
context機制。不過這種機制react並非特別推薦(不是特別推薦並不表明會在未來的版本沒有,只是說明可能會產生必定的弊端所以要慎用少用),context機制須要在上級組件(能夠是父組件的父組件)定義一個getChildContext函數以下:
getChildContext(){ return{ color:"red", } }
固然也能夠用事件機制
這回只能用事件機制了,雖然我以前分析過別的框架的事件機制部分均可以單獨拎出來用,可是這裏面實際上有好多方式。
我首先試了一下js-signals這個庫,這個也是React團隊使用的,用起來也還簡單,npm install signals
以後,咱們能夠單獨寫一個Signal文件:
const signals= require('signals'); var Signal = { started : new signals.Signal() };
咱們能夠把接收事件的函數定義在組件B中:
onStarted(param1, param2){ alert(param1 + param2); } constructor(props){ super(props); Signal.started.add(this.onStarted); //add listener }
而後在組件A中(注意dispatch的時候要保證B已經被構造出來了):
handlethis () { Signal.started.dispatch('foo', 'bar'); //dispatch signal passing custom parameters } render(){ return ( <button onClick={this.handlethis}>發射事件</button> ) }
其實還有不少相似的組件,固然咱們本身寫一個功能弱的也不成問題,更多的方式,這篇文章介紹的不錯。
mixin是一個飽受詬病的東西,另外蛋疼的是在ES6的寫法下也不能用,筆者如今寫react的時候都已經不用了,因此這裏進行簡單介紹。
咱們能夠經過在createClass的時候傳入一個mixins數組,這個數組裏是咱們的一些通用的方法:
React.createClass({ mixins:[method1,method2] //... })
這在ES6的class形式下是不能「直接」使用的。
ES7 的 decorator,做用就是返回一個新的 descriptor,並把這個新返回的 descriptor 應用到目標方法上。稍後咱們將會看到,decorator 並不是只能做用到類的方法/屬性上,它還能夠做用到類自己。
固然,這個我只言片語確定說不明白的,這個我要推薦淘寶前端團隊的這篇文章。
另外,core-decorators這個庫值得關注,它裏面有一個mixin方法用於實現mixin,原理就是用了ES7 decorator,實現起來也不是很是複雜。
最後,提醒一下ES7 decorator雖然很酷,可是目前還處於提案階段,雖然藉助babel咱們已經能夠體驗了,可是距離真正支持還有一段距離
這是一個頗值得一提的話題。
咱們應該據說太高階函數,這種函數接受函數做爲輸入,或者是輸出一個函數,好比map、reduce以及sort等函數。
一個高階組件只是一個包裝了另一個 React 組件的 React 組件, 這種包裝一般有兩種方式:
一、屬性代理(Props Proxy):高階組件操控傳遞給 WrappedComponent 的 props,
二、反向繼承(Inheritance Inversion):高階組件繼承(extends)WrappedComponent。
高階組件的功能主要有如下幾點:
一、代碼複用,邏輯抽象,抽離底層準備(bootstrap)代碼
二、渲染劫持
渲染劫持主要經過反向繼承來實現,咱們能夠選擇是否渲染原組件,也能夠改變原組件的渲染結果(注意:咱們經過 var elementsTree = super.render()
能夠拿到原組件的渲染結果,而後咱們能夠改變props以後,經過原生cloneElement方法建立出新的節點樹)
三、State 抽象和更改
所謂抽象state的目的,就是將原組件做爲一個純粹的展現型組件,分離內部狀態,將state交給高階組件來控制。好比:咱們能夠抽象出一個控制input的高階組件,從而不用在input中來有不少控制state的代碼。
四、Props 更改
咱們能夠讀取、增長、編輯、刪除被包裹組件的props
我在這裏沒有給出代碼,爲了不文章過於冗長以及和網上其餘專題文章大部分重複,我主要是進行一些總結,具體內容我這裏仍然是推薦一篇文章
PureRender這個概念實際上和純函數有關,Pure指的是對一樣的輸入(對於react來講就是props和state)老是獲得相同的輸出,針對這個問題,React有一個shouldComponentUpdate
鉤子,這個鉤子默認返回true,用於props或者state改變或者接收到新的值時候,能夠供用戶重寫,這樣在接受到相同的props的時候咱們就能夠防止其從新渲染。
PureRenderMixin在這個時候要派上用場了,這是一個可以實現上述功能的官方插件,react是這樣介紹它的:
If your React component's render function renders the same result given the same props and state, you can use this mixin for a performance boost in some cases.
其實是經過一個淺比較來肯定是否是該被渲染,這其實是一個性能上的權衡和妥協,深比較真的是耗費太多(咱們在下一節會提出一個更好的解決方案)。
寫法也比較簡單:
import PureRenderMixin from 'react-addons-pure-render-mixin'; class FooComponent extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } render() { return <div className={this.props.className}>foo</div>; } }
咱們能夠在這裏查看更多信息。
在傳遞數據的時候,咱們能夠用Immutable Data進一步提升性能。
Immutable.js定義了不可變對象,一個數據結構(MapListArraySet)一旦被定義,就不可變了,咱們把它若是用於state,那麼每次變化的時候須要將state整個從新賦值。
上文提到,在shouldComponentUpdate使用PureRenderMixin因爲性能權衡咱們只能使用淺比較,可是若是咱們用了Immutable.js,咱們有更好的方式:直接用=== or is
就能夠判斷,由於Immutable.js比較的是兩個對象的hashCode或者valueOf,而且內部使用了trie數據結構(好比字典樹)來存儲,所以性能很高。
另外,因爲Immutable.js中提供的數據結構是不可變的,咱們不用擔憂js中源對象跟隨引用對象的變化而變化的問題,也不用考慮函數中所謂的引用賦值,這給咱們的編程帶來了不少方便。
固然也有不方便的是Immutable.js的數據結構並不能和原生的數據結構混用,所以寫法上須要格外注意,關於更多資料請看這裏.
生命週期讓react的組件變得功能很是強大而且複雜,從而難以維護,而有的時候咱們又要常常寫一些自身沒有狀態,只從父組件接受props的組件,這種組件能夠提升react的渲染性能,也被官方推薦。
const HelloWorld = (props) => <div>{props.name}</div> ReactDOM.render(<HelloWorld name="HelloWorld" />,App)
簡單,高效,在有些不須要改變的地方,好比沒有用戶交互純聲明性質的內容,能夠用無狀態組件。
咱們想要讓效率更高,還要注意的一點就是要照顧react的diff算法,react雖然有一個複雜度僅爲O(N)的diff算法,可是這個算法也不是萬能的,咱們要想讓react效能最大化,就要去照顧這個diff算法。
總的說來,這個diff算法大概有三點實現概要:
對兩棵樹進行比較,react認爲,對節點的跨層級操做移動較少,因此只會對相同層級的dom節點進行比較,即同一個父節點下的字節點,當發現節點已經不存在時,就會刪除節點,當發現節點新增時候,就會插入節點。
爲了迎合這個策略,咱們儘可能不要對dom節點進行跨層級操做(好比把某一個字節點轉而掛在到某一個孫節點下面),由於這樣效率是比較低的。
對組件之間進行比較:若是是同一個類型的組件,按照第一條策略進一步比較虛擬dom樹;若是不是,就將該組件判斷爲dirty,從而替換全部字節點;對於同一類型的組件,有可能其虛擬dom樹沒有發生變化,若是可以確切知道這一點,那麼就能夠節省大量diff的操做時間,所以,react容許用戶經過shouldComponentUpdate鉤子來判斷組件是否發生變化。
爲了迎合這個策略,咱們可使用上面提到的PureRender或者Immutable.js。
當節點處於同一個層級,react提供了插入、移動、刪除操做,這裏主要指類似節點,好比<li>標籤,所以react容許開發者將同一個層級的節點添加惟一key進行操做,同一個key認爲是相同節點。以後react有一套本身的算法規則,對節點進行移動操做以達到要求(具體能夠參考「深刻react技術棧」176頁)。
爲了迎合這一規則,咱們要給li標籤等添加一個key(實際上已經被react強制),另外,在開發過程當中儘可能減小將最後一個節點移動到第一個的狀況,由於這個時候react要進行不少的移動操做。
對於各類動畫來講,緩動體驗通常是:linear < ease淡入淡出 < spring彈性動畫\cubic bezier貝塞爾曲線
。
動畫的方式有css動畫和js動畫,可是不少時候咱們都是一塊兒用的,因此區分的太詳細彷佛必要性也不大。
實際上動畫常常是筆者比較忽視的一個方面,因爲還沒畢業,大多時候都是本身作小東西,最後動畫就成了無關緊要的環節,另外如今的各類動畫庫不少,方便到只須要一個class、只寫一行代碼就能夠作出相對過得去的效果,本身也就疏於探索。
這部份內容主要推薦一些成熟的動畫庫。
首先是ReactCSSTransitionGroup,這個動畫庫提供了一些生命週期鉤子,咱們能夠利用此加動畫,具體學API的過程至關簡單,我相信看懂上面各個部分的同窗直接按照給出的連接確定能順利學會。
還有react-smooth動畫庫,這也是一個比較有意思的動畫庫,寫法相似css的多關鍵幀動畫。而且幾種緩動函數動畫這裏都能實現。
react-motion也是一個值得推薦的動畫庫,若是想用spring動畫這個彷佛是更好的選擇。
另外,不說react,還有一個讓我印象深入不得不提的就是vivus.js這個svg動畫庫,不得不說真是酷斃了。