慢牛系列三:React Native实践

上次发布了我的慢牛股票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上,文章写的一般,见谅!欢迎大家评论留名^_^

关于我的兼职创业历程

慢牛系列一:如何抓取股票数据

慢系系列二:前端技术选择

posted @ 2015-11-26 08:59  OOLi  阅读(8120)  评论(12编辑  收藏  举报