上次發布了我的慢牛股票APP之后,有園友反饋有點卡,這個APP是基於Sencha Touch + Cordova開發的,Sencha本身是一個比較重的框架,在Chrome里運行性能還是不錯的,但是在Android的WebView里,性能受限於機器的配置,在我的小米2s里表現還行,在小米4s里開起來比較流暢,但是Android機型相比IOS太多樣了,Sencha Touch在iOS里表現不錯,不過我還沒編譯iOS版本。
后來我又試着用了下Ionic框架,基於AngularJs開發,這個框架要輕量,在慢牛股票的微信公眾號里試着開發了幾個頁面,感覺很不錯,本來想用AngularJs重新做一個APP,但是后來又接觸了React Native,試着用了下,React Native的編程體驗類似於Sencha Touch,完全的組件化,JSX的語法很有意思,逐步習慣以后,用起來也很順手。
有興趣的朋友可以對比下這三個方案,我留下這個三個方案的下載地址:
慢牛股票APP(Sencha Touch+ Cordova)方案
慢牛微信公眾號(Ionic方案)
Demo(React Native方案)
截圖動畫:
React Native相比上面兩種,體驗更順暢,當然,原生的應用肯定是比在WebView里體驗好的,ReactNative結合了原生開發和Web開發優勢,不過,React Native現在對Android的支持不算太好,比如動畫。
ReactNative的Flex布局,組件化思想很不錯,上手快,不過因為最終顯示的是原生,所以還是要多了解下原生的開發,必要時自己做橋接,比如我在做K線圖,苦於找不到相關的組件,只有自己橋接了MPAndroidChart。
Demo主要實現了幾個我認為比較關鍵功能:
1、導航
2、列表
3、下拉刷新
4、圖表
這幾個功能點實現以后,后面的工作就輕松得多了,比如表單提交,第三方登錄,設置等等頁面。
下面說下這幾個點的實現方式,
1、導航:
側滑菜單:
熟悉原生開發的同學一看就知道,這是原生的DrawerLayout,通過RN橋接過來,
很滑,很順,之前用CSS3在WebView里做過側滑菜單,當然比不上這個啦!
頁面切換:
利用了RN的Navigator組件,這個組件實現了一個Stack,每次新開頁面就是向Stack里Push新的UI導航信息,然后在Render時判斷要顯示哪個UI組件,這個導航組件會把歷史UI移動可視范圍之外,利用Opacity把UI設置為透明,然后絕對定位到可視范圍之外,返回時,再把歷史UI移動可視范圍,這樣,歷史UI的狀態還是保持的,這樣的導航在Web上也是可以借鑒的。
這個導航還可以設置轉場動畫,看源代碼,確實是實現了不少的動畫,包括PushFromRight,FloatFromRight,FadeAndroid等,但是設置后不起作用,只有FadeAndroid有效果,但是動畫一閃而過,不順滑,這里對Android的支持不太好,或者是我的哪里設置不對?
Tab切換:
Tab切換做起來比較容易,不過有一點,在頁面切換過程中,不要重新Render每個Tab對應的內容,重新Render會清除頁面的當前的狀態,比如滾動條的位置等,而且性能差,借鑒Navigator,把非焦點的Tab頁面移動可視范圍之外即可。
Demo中按鈕的圖標都是矢量的,圖標字體的優點不用說了,原生系統也可以用圖標字體了,對不喜歡用圖片的同學真是一個喜訊,一想到要做那么多圖片,要考慮每種分辨率,就頭腦哇,這里用了github上的一個開源組件react-native-icons。
貼段代碼:
render: function() { var navigationView = ( <SideMenu onItemSelected={this.onItemSelected}/> ); return ( <DrawerLayout ref="drawerMenu" drawerWidth={300} drawerPosition={DrawerLayout.positions.Left} renderNavigationView={() => navigationView}> <Navigator ref='navigator' debugOverlay={false} style={styles.appContainer} configureScene={(route) =>Navigator.SceneConfigs.FloatFromRight} initialRoute={{name:'main',component:(<TabPanel/>)}} renderScene={(route, navigator) => { return (route.component); }} /> </DrawerLayout> );
2、列表:
Demo里我實現了一個股票的列表,列表拆分成了List組件和ListItem組件,利用React開發,組件化思想很重要,這在之前用Sencha時也有這樣的體驗。
列表這里沒有用ListView組件,是用ScrollVIew包了一個ListTtem的列表,如果數據量比較大,或者比較復雜列表,可以用官方的ListView來做,性能會比較好,每次更新只會更新變化的列表項,也提供了分類,設置表頭和表尾等等。
貼段代碼:
createRows:function(){ return this.state.myStockList.map(function (obj) { return (<StockItem key={obj.code} id={obj.code} data={obj}></StockItem>); }); }, render: function() { return ( <SwipeRefreshLayout ref={(control)=>{this._view=control}} style={styles.scrollView} onRefresh={this.reloadData}> <ScrollView style={{flex:1}}> {this.createRows()} </ScrollView> </SwipeRefreshLayout> ); }
3、下拉刷新:
在github上,有不少的下拉刷新組件,比如;react-native-gifted-listview,但是都是iOS上可以下拉,在Android上有Bug,文檔上這樣解釋:
Pull-to-refresh in Android (tried to implement it but it seems that onResponderRelease event is not catchable yet in Android ListView - React-Native
關於如何在RN里響應手勢,我還沒怎么了解,后面再動畫方面多研究下,因為了解下原生控件的橋接,所有就偷懶了,把原生的SwipeRefreshLayout控件做了橋接,包裹上ScrollVIew,就可以實現下拉刷新了,在JS里響應SwipeRefreshLayout發送的刷新事件,同時開放一個關閉刷新的接口,JS端獲取數據更新后,關閉刷新狀態。
4、圖表
圖表這里,我花的時間最多,目前也不是很完善,只不過,可以顯示K線了,但是在交互上還是要加強。
之前在Web端做過兩次K線圖,一個是Sencha的,一個是D3的,用了RN以后,之前的圖表都用不上了,也考慮過套個WebView,用D3來做,但是效果肯定不好哇,最后還是學習了原生開發,學習了原生UI的布局,組件的繼承架構,學習使用MPAndroidChart組件,如何橋接原生組件等等,收獲不小,現在開發一邊打開Android Studio,一邊打開Sublime。。。
原生的組件,View是所有UI組件的基類,而 ViewGroup是容納這些組件的容器,其本身也是從View派生出來的,RN提供了兩種ViewManager類用來橋接這兩類組件,ViewManager管理組件的創建,布局,以及屬性設置,事件觸發。
貼段代碼:
android端(橋接圖表代碼片段):
public class MPBarLineChartManager extends SimpleViewManager<BarLineChartBase> { private String CLASS_NAME="MPBarChart"; @Override public String getName() { return this.CLASS_NAME; } @Override protected BarLineChartBase createViewInstance(ThemedReactContext reactContext) { BarChart chart=new BarChart(reactContext); return chart; } @ReactProp(name="touchEnabled",defaultBoolean = true) public void setTouchEnabled(BarLineChartBase chart,boolean enable){ chart.setTouchEnabled(enable); }
...... }
js端:
var { requireNativeComponent,PropTypes } = require('react-native'); var iface = { name: 'BarChart', propTypes: { data:PropTypes.object, touchEnabled:PropTypes.bool, ...... scaleX: PropTypes.number, scaleY: PropTypes.number, translateX: PropTypes.number, translateY: PropTypes.number, rotation: PropTypes.number, }, }; module.exports = requireNativeComponent('MPBarChart', iface);
這個圖表的橋接項目我發布到了github上,目前實現了對柱狀圖,線形圖,以及組合圖表的橋接。
https://github.com/hongyin163/react-native-chart-android
有希望用圖表的同學可以利用下,要是能繼續幫忙增強就更好啦!
學會了橋接原生組件以后,我就陷入了一種模式,只要在RN里實現有難度的,我就想橋接一個原生的Android組件,雖然知道這樣不太好,因為如果用了太多原生的組件,后續的遷移麻煩了,當然最好利用官方提供的Android和IOS都支持的組件,最好是用平台無關的方式來做,這里最大的感觸是,RN提供了一種途徑,讓js可以和原生組件交互,業務邏輯寫在js里,利用原生組件呈現,如果有原生開發的同學支持,那么利用RN開發就方便多了,提供一致的組件庫,一套代碼就可以在兩個系統上運行了,這樣就不僅僅是learn once write anywhere了。
關於性能優化:
1、延遲加載
如果一次加載太多的組件,RN需要等全部組件渲染完成才顯示出來,會有白屏,所有每次加載組件最好少一些,比如先構建頁面的框架,框架渲染完成后再加載數據,創建基於數據的組件,或者利用setTimeout延遲加載其他的組件。
2、使用setNativeProps
React把組件當成狀態機,通常是用setState方法,改變組件狀態,頁面會重新渲染,但是重新渲染性能比較差,setNativeProps可以繞過這個過程,直接修改原生組件的屬性,或者調用原生的組件的API,性能就好多了,但是狀態同步方面就要多考慮下了。
3、實現shouldComponentUpdate方法
React在組件渲染時會有個diff算法,如果前后Virtual DOM狀態改變,會重新渲染組件,如果實現shouldComponentUpdate方法,返回false,就會避免不必要的diff計算和渲染。
關於React,這篇文章把他的前世今生都說了,寫得很好:通往全棧工程師的捷徑 —— react
React真是無所不在了,React-DOM,React-Canvas,React-Native,看起來前途無量哇!
最后,大家可以關注慢牛股票的微信公眾號:
發送react,可以獲取demo的apk文件,安裝體驗。
這個項目的源碼后續會提交到github上,文章寫的一般,見諒!歡迎大家評論留名^_^