2016年1月9日 React Native实战经验分享会(北京)分享记录




  • 2016年1月9日,由React Native 中文网主办的实战经验分享会北京站在751时尚设计广场举行,主讲人React Native中文网的站长天地之灵为大家分享了React Native实战的感悟和经验教训。

    0_1454062030640_ReactNativeExpShareImg.001.jpeg


    天地之灵:我们是杭州的一个团队,主要面向初创团队和转型互联网+的团队提供技术支持和项目外包服务,我们从去年9月底开始使用React Native不断的开发新项目,目前我们已经全面使用React Native作为技术框架了,今天我们给大家介绍一下我们在开发当中实践的一些总结。

    0_1454062218582_ReactNativeExpShareImg.002.jpeg

    天地之灵:很多人都问我们这么一个问题,我们新的一个项目,我们到底该不该选择用React Native,或者一个老项目,我们有没有必要在它当中使用一部分的React Native,或者说React Native这个东西到底哪儿好哪儿不好,我想这可能是咱们大家现在都很关心的问题。

    0_1454062229041_ReactNativeExpShareImg.003.jpeg

    天地之灵:首先我们来大致定义一下,React Native本质上到底是一个什么东西,他最终解决的是一个什么问题。

    0_1454062479808_ReactNativeExpShareImg.004.jpeg

    天地之灵:React Native最本质解决的问题,就是使用脚本编写移动应用。用脚本编写移动应用的目的,就是实现业务层的一次编写、多处运行,另一个好处就是实现自更新,跳过发包、审核、用户主动更新这样的流程,快速的把产品的迭代更新发放到最终用户。

    天地之灵:除了业务层的更新,我们也希望我们的视图布局,也是可以能够在不同平台间尽可能复用。

    天地之灵:React Native为了达到这样的目的,它做的一个基础的事情就是支持脚本和原生的交互,这是通过所谓“桥接层”(Bridge)来实现的,一方面来说,我们可以用原生构建视图组件,提升性能和交互体验,另一方面可以在脚本中访问系统API,譬如网络、重力感应等等。

    0_1454062493216_ReactNativeExpShareImg.005.jpeg

    天地之灵:这样给我们带来的好处其实也很明显。最主要的好处就是体现在成本(包括开发测试成本,也包括团队组建、学习成本)上,除了业务层代码跨平台以外,我们还有大量的web开发积累下来的设计模式、库、和工具可以使用。

    天地之灵:当然,使用原生开发,也是有其好处的,毕竟是最直接的方式,能够获得最佳的用户体验,并且真正原生的资料积累、组件也非常多。

    0_1454062505474_ReactNativeExpShareImg.006.jpeg

    天地之灵:React Native的本质就是为了解决用脚本编写移动应用这一个问题,但仅仅这样听起来,好像类似的方案有很多,其中有很多都是我们大家耳熟能详的。除了这些,我们知道还有Ionic等方案应用也非常普遍。那么,我们为什么要选择React Native呢?下面我们就将React Native和这些方案依次进行对比。

    0_1454062510832_ReactNativeExpShareImg.007.jpeg

    天地之灵:首先,我们和HTML5的方案相比。使用React Native,最大的好处是,流畅度是非常明显的提高,内存占用也是明显降低,尤其是在一些列表组件中。另一方面,尽管HTML5可以将触控事件在JavaScript事件机制中处理,但我们以往实战的体验表明这样做并不能得到一个及时流畅的操作体验。另一方面,React Native和原生代码的整合要容易的多,不论是把原生组件在React Native中应用,还是把React Native实现的部分作为一个组件放入原生应用中。

    天地之灵:当然,HTML5的方案也有其好处,一方面是web组件生态更完善,社区资源更丰富,另一方面HTML5的开发团队也更易组建,开发人员也更有经验。除此以外,尽管理论上只要重新实现一套“桥接层”就可以让React Native覆盖一个新的平台,但考虑到各种常用组件的封装、整体开发、测试的成本,React Native要想覆盖一个新平台还是有不低的成本,而HTML5则没有此困扰。

    0_1454062543423_ReactNativeExpShareImg.008.jpeg

    天地之灵:这三个方案中,前两个方案都是封闭式的方案,而Qt则作为商业开源的方式运作。后两个方案都采用了“原生自绘”的方式实现UI,与它们相比,React Native作为自由开源软件,在社区中分享组件的习惯更为普遍,因此社区生态更完善、更活跃,发展更为迅速。并且JavaScript语言的受众也要比C#和ActionScript更为广泛。除此以外,“原生自绘”的方案,在内部嵌套原生组件就会十分困难。

    天地之灵:当然,这三个方案的一个主要好处就是IDE和官方的文档样例都非常健全,并且很容易找到付费的技术支持。

    0_1454062548664_ReactNativeExpShareImg.009.jpeg

    天地之灵:最后我们重点说一下NativeScript,它也是在2015年发布并得到广泛关注的跨平台原生应用开发方案。它很多思想和React Native都非常接近,发布比React Native还略早,并且更快的覆盖了Android平台。

    天地之灵:NativeScript的一个主要思想是它仍然试图达到"Write once, run anywhere",这实际上意味着只能使用各个平台的功能子集。另一方面,它试图让原生的所有细节都直接对JS暴露,让JS可以直接操作视图对象,这意味着它的JS脚本和原生UI必须在同一个线程(主线程)执行,并且在这个线程的调用栈里可能反复的出现跨语言的调用。这还带来的一个影响就是跨语言的生命周期、引用管理会有很大的困难。

    天地之灵:而React Native则推荐的是“Learn once, write anywhere”思想,也就是说我们需要学习一种开发方式,就能够给每个平台开发应用,而使得最终结果、用户体验更接近这个平台的用户习惯。另外React Native基于React构建,目前社区也有较多的设计模式、组件库可以被直接应用。

    天地之灵:目前我们在github上看到的,React Native的关注度、贡献者、以及发展速度已经远远超过NativeScript了。

    0_1454062564705_ReactNativeExpShareImg.010.jpeg

    天地之灵:刚才我们看了React Native和现有框架的一些对比,现在我们来总结一下React Native自身的优劣,我们先来看一下优势。

    0_1454062569480_ReactNativeExpShareImg.011.jpeg

    天地之灵:React Native的许多核心优势都与它基于React构建有关。React在2015年末已经成为了仅次于AngularJS的第二流行前端框架,并且是可用组件最多的前端框架,它为组件化而生。除了社区分享外,在团队内部,这也非常有利于模块的分享和复用。

    天地之灵:React Native最早仅仅是思想上基于React构建,并没有在代码层面上完成协调一致,但从0.14起,它已经可以和React共同使用而不再冲突,从0.18开始,它本身已经直接依赖了主线的React,摆脱了与HTML DOM无关的任何库的不兼容问题。

    0_1454062577064_ReactNativeExpShareImg.012.jpeg

    天地之灵:React基于函数式思想和Immutable思想设计,这带来的最大好处就是React组件在每一个状态下产生的结果,以及每个行为带来的变化,都是可预测、可测试的。这对于做视图层的自动化测试来说非常方便。

    天地之灵:因为基于虚拟DOM机制和单向数据流构建,这对于小型项目来说性能方面并没有太大区别,但随着项目规模的扩大,因为每一次动作之后界面的变化都可以预期,性能也不太会变得不可解决的过于糟糕。

    0_1454062584439_ReactNativeExpShareImg.013.jpeg

    天地之灵:同样受益于虚拟DOM机制,React Native的“桥接层”与许多其它方案不同,它不再在脚本层直接持有原生对象的引用,并在每次调用方法的时候反复将参数序列化转换。相反,大部分情形下,我们直接在JS层计算出虚拟DOM的差异,然后将整个DOM树中少量的变化批次打包,以异步的方式交给原生去更新真正的视图结构,这样规避了跨语言的循环引用带来的一系列问题以及频繁跨语言的性能问题。

    天地之灵:异步的线程模型还带来了一些额外的好处,其中最重要的一条就是:当脚本运行缓慢的时候,脚本的运行性能不影响原生视图本身,这样使得包括触控处理在内的许多操作仍然保持流畅。

    0_1454062599624_ReactNativeExpShareImg.014.jpeg

    天地之灵:因为React Native所用的视图,真正对应着我们原生的UIView或android.view.View,并没有做太多自绘的逻辑,所以已有的原生组件,譬如百度地图、视频播放之类的组件,可以很简单的将它封装为Native Component加入React的视图树,在React Native应用程序中应用它们,我们所要做的基本上就是把属性传递给它,再将事件回发给React Native,最终的结果不论是展示上还是操作上都和原生应用中直接使用组件完全一致。

    天地之灵:反过来也是如此,其实Facebook自己早期在应用RN的时候,在大部分项目中也并非整个项目由React Native开发,大部分组件还是由原生去做,这时我们只要在原生去创建一个RootView对象,它内部对应到React Native的一个组件,而外部则作为一个普通的原生视图(UIView或android.view.View),可以直接嵌入原生应用之中。

    0_1454062638496_ReactNativeExpShareImg.015.jpeg

    天地之灵:React Native提倡的一个思想就是“Learn once, write anywhere”,真正的“Write once, run anywhere”意味着只能使用多个平台的特性子集,也无法保证在每个平台上都具备真正适应这个平台的用户体验。而React Native则天然被设计成可以为特定平台优化用户体验,我们在React Native内置的组件中就能看到很明显的这一特征。

    天地之灵:举例来说,iOS的ScrollView通常在滚动方向可以弹性的拖动一截,这既是iOS的UIScrollView天然具备的特性,也是iOS这个平台用户最适应的体验。而这一点在Android的ScrollView就会略有不同。

    0_1454062649232_ReactNativeExpShareImg.016.jpeg

    天地之灵:React Native整体的社区生态是拥抱新ES标准的,这样我们可以享受很多新语法带来的便捷特性,这可以使得我们的代码干净整洁,避免很多问题。

    天地之灵:另一方面,虽然React Native的组件和DOM并不兼容HTML,但在一些其它的API上,它都在尽可能兼容HTML5的API,譬如定位、网络访问等等,这使得很多第三方的js库在React Native下依然可以直接使用(如socket.io等等)。

    天地之灵:React Native可以把Chrome当做自己的脚本运行环境,这使得我们甚至不需要为React Native准备独立的IDE,只需要使用Chrome强大的开发工具(也可以通过插件在JetBrain等IDE中调试),就可以构建最方便的调试环境。

    天地之灵:值得一提的还有Angular社区的一件事情,Angular2也在尝试移植到React Native的bridge层上。所以我们将来有可能可以使用Angular编写我们的业务层,而原生组件、API等等依然使用React Native社区的生态。

    0_1454062681905_ReactNativeExpShareImg.018.jpeg

    天地之灵:React Native尽管如此强大,但也并非完美,这也是在我们实际项目做技术选型时都需要考虑的方面,下面我们来聊一下实践中发现的一些缺陷。

    0_1454062687249_ReactNativeExpShareImg.019.jpeg

    天地之灵:首先说一下性能方面的问题。因为JS和原生之间的交互是异步执行的,因此当JS脚本执行缓慢的时候,所有需要JS配合进行的逻辑都可能会随之变慢,但是还有一部分触控操作是直接在原生处理的,因此即使没设计到网络访问,有时候用户体验也像是在和一个存在延迟的服务器进行交互,譬如列表或其它数据的刷新是缓慢的,或者输入框在输入不合法的字符之后会隔一段时间才弹回等等。

    天地之灵:还有一个常见的问题就是在一瞬间创建大量视图,这在React Native中常常会带来卡顿。这个问题多见于导航器切换界面的时候,尤其是在比较老的安卓设备上。官方文档提供的一种解决方案是在播放切换动画的时候临时用占位视图占位,在动画播放完毕以后再绘制详细的界面,但这显然不是最佳的体验。这一点有可能会在将来通过用一些二进制协议来进行JS-原生交互来大幅优化。

    天地之灵:还有一个比较突出的问题是ListView如果在滚动的时候有数据的刷新,滚动性能会很明显的变得糟糕。这个问题官方也在着力于解决中。

    0_1454062693440_ReactNativeExpShareImg.020.jpeg

    天地之灵:React Native从去年9月的0.11、0.12,到现在已经快速迭代到了0.18版本,并且每个版本都有很多更新。这个过程中还是带来了不少形形色色的问题,到目前为止,我们React Native中文网的社区中,绝大部分的讨论都是每个版本带来的一些新BUG等等,虽然大都有解决方案,但也给开发带来了较大困扰。

    天地之灵:除此以外,目前无限的列表视图还是存在内存方面的问题,以至于很容易导致闪退。这在实现某些类似朋友圈、照片墙等等界面的时候往往需要,目前我们实际的解决方案是这一类界面只能暂时选择原生去进行开发。

    0_1454062698041_ReactNativeExpShareImg.021.jpeg

    天地之灵:生态方面,React Native得到广泛关注,也就是去年9、10月份开始,现在npm上很多组件还处在“占坑”的状态,功能大都不全,并且还有一些因为缺乏维护,存在兼容问题。另外,暂时还没有一套成型的可用组件库,社区这边在关注material ui的实现,但其功能仍不完整。

    0_1454062703129_ReactNativeExpShareImg.022.jpeg

    天地之灵:除此以外,还有一些其它的缺陷,譬如系统兼容性最低需要iOS 7.0+,Android 4.1+,大约需要放弃3%-5%用户;安卓版本的基础包较大(6.9M);不支持Z排序、table布局等等。

    天地之灵:另外,如果同时使用了原生触控处理和JS触控处理,有的时候存在JS试图接管触控,但原生已经开始处理触控时,甚至已经表现出结果了,此时可能存在界面异动,这在ScrollView中内的视图中包含JS触控处理的时候尤为明显。

    天地之灵:前面还已经提到,尽管理论上只要重新制作一个“桥接层”就可以覆盖一个新的平台,但重新实现所有组件和API,并测试稳定,仍然需要极大的代价。官方2015年3月发布了iOS版本,9月份才发布Android版本,WP版本可以说是更加遥遥无期。

    0_1454062713928_ReactNativeExpShareImg.023.jpeg

    天地之灵:另外,尽管JSX对开发者十分友好,但对于设计人员并不友好,很难为它设计出一个所见即所得的开发工具。同样,学习React Native仍然需要掌握一系列知识,也并不能做到傻瓜化开发。

    天地之灵:现在React Native会把整个应用的所有js文件打包成一个bundle,如果要进行更新,则需要替换整个js文件,这在实现自更新的时候会带来较大流量的开销。从github上我们发现官方正在做一些事情改善这一点,但目前还没有发布。

    天地之灵:还有,由于快速更新带来的问题,以及社区组件的缺乏,使用React Native制作一个完整的项目,至少现阶段并不能脱离原生开发人员的支持。

    0_1454062722120_ReactNativeExpShareImg.024.jpeg

    天地之灵:总结一下我们的观点,就是尽管React Native目前尚为年轻,但随着生态进一步发展,它必将成为大部分高质量应用开发的首选方案。因为使用脚本开发确实是能带来足够大的好处的,而对比所有的类似方案,React Native确实是其中最强大、最自由,以及性能和体验最好的。

    天地之灵:当然我们要强调的是,在现阶段,不推荐大范围将现有项目使用React Native重构,因为目前这还是要花费较多时间的事情,存在较大风险。但是对于新项目、以及原生项目中的一些新模块,尤其是之前使用React开发的项目打算迁移移动版,其实应该考虑一下React Native了。

    天地之灵:这一部分演讲就到这里,谢谢大家。



  • 明天(2016年1月30日)我们的广州分享会将在广州天河区羊城创意产业园举办,届时我们将会进行现场直播,欢迎线上围观:)


登录后回复