为什么要选择我们公司我不选择 React Native

Facebook:我们是如何构建第一个跨平台的 React Native APP
-10%-22%-50%-77%
今年早些时候,我们介绍过。 React Native带来的是用web方式的React - 自声明式的UI组件和快速的开发迭代来完成手机平台的功能,然后为了保持速度、保真性、并达到原生的体验。今天我们很高兴发布。
在Facebook我们已经应用React Native在发布的产品有超过一年的时间了。几乎是整整一年之前,我们的团队开始规划开发。我们的部门是创建一个新的APP来让数百万的Facebook广告主来管理他们的账号并能创建新的广告。在完成的时候,这不仅仅是FB的第一个全React Native APP而且是第一个跨平台的APP。在这篇文章里,我们希望能和你分享我们是如何构建这个APP,React Native是如何让我们更快的,还有这个过程中我们的经验。
选择React Native
不久前,React Native还是一项新的技术,还没有被一款正式的产品应用过。并且开发这样一个新的APP会有很大的挑战,它超过了潜在的好处。
首先,我们初始的团队里有三个产品工程师已经对React很熟悉。另外,这个APP需要处理大量复杂的商业逻辑和精确的处理不同的广告格式、时区、日期格式、货币、汇率等等诸如此类。而大部分已经用JavaScript来实现了。全部用Objective-C编码并稍后用java实现Android版本的想法也并没有被赞成,而且也并不高效。第三,用React Native将会很容易来实现大部分的UI,可以实现带数据的列表、表格、图表。产品工程师可以很快的实现这些效果,用React就可以了。
当然,一些特性的实现存在着挑战 - 比如,图片的编辑,用来让广告主缩放和剪切图片;地图视图,用来让广告主设定地理范围。另外一个是面包屑导航,帮助广告主来可视化的知道自己的层级位置。这些都提供机会让我们来推动这个平台的发展。
首先实现广告工具的iOS版本
我们的团队觉得首先开发iOS版本,也是为了和React Native的iOS版本校准一致。我们从后面的几个月时间里从3个增加到8个工程师。新加入的成员对React并不熟悉 - 其中也并不熟悉JavsScript - 但是他们都渴望构建一个伟大的手机应用来服务广告主,并且他们成长的非常快。
有经验React Native的iOS工程师帮助我们实现一些他们并没有在React Native中实现的特性,像提供访问相册。他们也帮助我们和其它FB已经存在的APP在使用的iOS库做关联,像认证、分析、崩溃报告、网络和推送提醒。这让我们的团队可以关注在产品上。
除了上面提到的,我们可以使用以前就写好的JavaScript类库。像,一个通过来传递数据到React应用的FB框架。另外的一系列库用来处理国际化和本地化,它能很聪明的实现时区和货币的调用。这些库的加载是在一个JSON的配置文件里,包括APP用到的iOS的本地关联文件,仅仅暴露很少的native代码。这让我们的库几乎不需要修改就能使用。
我们遇到的最大的挑战就是导航。为了导航广告主的广告和活动,我们想使用面包屑导航条。指引广告的创建流程,我们需要一个导向式的导航条。在最上面,非常重要的是需要使用合适的动画和手势操作,否则这个APP看起来还是像一个经过美化的website。
我们的解决方案是使用,是一个用React Native来实现的可定制化的组件。本质上,它是一个追踪一系列React组件的组件。它可以在组件之间基于按钮点击和按下时进行动画切换。它也具有可插拔式的导航组件,让我们来实现iOS风格的导航视图,以面包屑的方式来导航广告和活动,指引创建流程的步骤。这个导航条组件还能获取到动画的进度以及根据需要来调整动画的频率。这意味这所有动画,包括视图和导航条都可以通过JS来处理,而且测试的结果是仍然能够达到60fps。
只有一种情况下动画才会卡顿,就是当JS线程被一个大的操作占用时。当我们遇到这种情况时,基本上都是执行大量的数据获取操作造成的。必然的,当导航到新页面时需要加载大量的数据。当网络足够快是,动画可以很容易的被执行。我们的解决方案是延迟数据的获取直到动画执行完毕,这时就使用到了组件,同样是React Native的一部分。我们首先动画到一个新的视图,然后再用Relay来执行数据加载进程,这样就能自动的让需要的React组件实现自动渲染了。
开发Android版本
当iOS版本的广告管理工具接近开发完成时,我们开始着手Android版本的APP。移植React Native的Android是最好的方式来完成这个工作。幸运的是,React Native团队已经在上面做了很多的工作。自然的,我们想尽可能的复用更多的代码,因为大部分的视图都很相似。当然,也有一些地方需要做Android个性化处理来和iOS版本有所区别,比如,导航的元素或者是调用本地的UI元素像日期选择、开关等等。
幸运的是,React Native包的黑名单特性和React的抽象结构帮助我们最大化的重用代码来实现跨平台的功能。在iOS版本里,我们打包的时候忽略所有后缀名为.android.js的文件。对Android的开发,忽略掉所有后缀名为.ios.js的文件。现在我们可以实现同样的组件来同时应用Android和iOS,也可以个性化的编码在不同平台。替换掉 if/else 这种方式来判断平台,我们尝试重构每个平台的特定UI,这样就可以有Android和iOS的不同实现。在构建Android版本的过程中,大约85%的代码可以被复用。
另外一个挑战是怎么管理源代码的问题。Android和iOS的代码库在Facebook两个不同仓库。广告管理工具的iOS源代码在iOS仓库,Android版本的代码在Android的代码库。举例来说,像iOS版本的代码,我们想用一些Facebook的Android的依赖库,这些库却在Android的代码库存放。另外,Android的APP所有的编译工具,自动化,以及引入的其它插件都在Android的仓库。基于上面,Android的这些app要求重构这些已经存在的iOS代码来抽象具体平台的组件来调用各自的文件。我们是可以直接合并两个版本的代码到一起。但是这种方式却是我们不能接受的。
最后,我们决定指定iOS库为事实上的源代码库,因为iOS版本已经相对稳定。我们设定了定时任务许多次一天来同步iOS的JavaScript到Android的代码库。我们不鼓励在Android版本提交JavaScript代码,如果提交也是在iOS版本同步的提交一份。如果同步代码发现代码有差异,会记录一个任务用来进行后续的检查。
我们让iOS仓库的JavaScropt打包成可以在Android版本上运行的代码。这样我们的产品开发人员就可以接触到尽可能多的JavaScript代码而没有原生代码,也可以直接在iOS仓库直接修改和调试两个版本的代码。但是如果要构建Android的APP还是需要在Android仓库来执行,同样的操作也会在iOS APP - 测试两个平台的不同需要大量的额外工作。为了提高JavaScript开发者的工作流程,我们同样构建了脚本来下载合适的来自整合服务器的原生文件。对于大部分开发者来说就不需要复制一份Android的代码库了 - 他们就可以在iOS代码库开发完整的JavaScript代码,并且能比在Facebook的web流程中更快速的进行迭代。
我们学到的
React Native团队开发的进程和我们的APP一起,并且和他们一起调试本地化组件和API。这些组件将会为每个构建APP的人带来帮助。尽管我们必须自己来构建一些组件,用React Native代替纯原生的方式仍然是有价值的。我们不得不需要写这些组件,虽然在未来的一段时间里也可能不会被其它团队再次使用。
学到的另外一课是在分开的iOS和Android代码仓库工作是一件困难的事情,尽管使用了大量的工具和自动化。在构建APP的过程中,Facebook用过这样的模式,我们所有构建的自动化和开发进程都建立并围绕着它。然而,对于产品来说,用一份共享的JavaScript代码库,这种方式并不好。幸运的是,Facebook已经对所有平台都&- 只需要一份JavaScript的拷贝,同步那样的方式已经成为了过去。
另外学到的是关于测试。当做了修改,每一个工程师一定要在所有平台仔细测试,这个过程很容易出现人为的错误。但是开发一个跨平台的APP而且是用一套代码,这些是必须的。即便如此,由于测试不足导致的成本,远大于用React Native开发的成本和能重用跨平台代码的成本。请记住,这里说的不仅仅是产品工程师;同样包括React Native平台的Objective-C和Java工程师。他们的工作不是限制在原生语言。同样包括JavaScript - 举例来说,组件API和部分分享部分的实现。ISO工程师一般来说不必一定要测试修改后的Android的代码,对应Android工程师也是一样。这种文化缺陷需要我们用时间和努力来消除,随着时间的推移,我们会越来越稳定。
在每次修改整合版本的时候我们也会标记问题。这些标记的东东可以获取iOS版本的问题,同样的对Android也适用,我们连续的整合版本不会在iOS修改的时候来运行Android的测试,反过来也一样。这样工程师就可以更多精力来解决问题,并且不用经常的来重启APP。
随着上面说的和做的,我们的债算还完了 - 我们可以运行Facebook的第一个完全的React Native APP在两个平台上,具有原生体验,同样的JavaeScript工程师团队。他们当中有些还并不熟悉React, 但是他们在5个月之后就开发出了具有原生体验的iOS版本,之后三个月,我们又发布了Android版本。
上一篇:下一篇:
评论功能关闭
根据国家法律法规要求,本站暂时关闭文章评论功能。开放时间不确定。我们将谋求一种可以让大家更好的发表意见的方式。
根据国家法律法规要求,只有实名认证后才可以发表评论。
今年早些时候,我们介绍过iOS版的React Native。 React Native带来的是用web方式的React - 自声明式的UI组件和快速的开发迭代来完成手机平台的功能,然后为了保持速度、保真性、并达到原生的体验。今天我们很高兴发布React Native的Anroid版本。 在Facebook我们已经应用React Native在发布的产品有超过一年的时间了。几乎是整整一年之前,我们的团队开始规划开发广告管理APP。我们的部门是创建一个新的APP来让数百万的Facebook广告主来管理他们的账号并能创建新的广告。在完成的时候,这不仅仅是FB的第一个全React Native APP而且是第一个
分享到微信
打开微信,点击顶部的“╋”,
使用“扫一扫”将网页分享至微信。
请将我们加入您的广告过滤器的白名单,请支持开源站点。谢谢您。主题信息(必填)
主题描述(最多限制在50个字符)
申请人信息(必填)
申请信息已提交审核,请注意查收邮件,我们会尽快给您反馈。
如有疑问,请联系
CSDN &《程序员》编辑/记者,投稿&纠错等事宜请致邮
你只管努力,剩下的交给时光!
如今的编程是一场程序员和上帝的竞赛,程序员要开发出更大更好、傻瓜都会用到软件。而上帝在努力创造出更大更傻的傻瓜。目前为止,上帝是赢的。个人网站:。个人QQ群:、
个人大数据技术博客:
本文为携程技术中心投稿,版权归原作者所有,非经允许,请勿转载。
【作者简介】姚瑞琼,前端程序媛一枚。2014年毕业后加入携程火车票事业部,今年年初起至今,主要负责 React Native 方案在火车票业务线的实践,先后参与并负责汽车票 RN 独立版、携程 App 抢票 RN 版的开发迭代。
欢迎技术投稿、约稿,给文章纠错,请发送邮件至。
火车票作为携程体系下的重要环节,要兼顾良好的 App 用户体验及迅速的业务迭代,一个月左右一次 App 版本的节奏很难满足,而 React Native 跨平台、媲美原生 App 的用户体验以及无需发版的升级模式等等优势无疑使人眼前一亮。加上基础团队的 Ctrip React Native 框架对 RN 的性能优化、业务封装以及拆包发布等的大力支持,火车票现已上线将近 20 个 RN 页面,经历了携程 App 三个大版本的迭代与考验。本文将着重介绍 React Native 在携程火车票产品中的应用,以及在 RN 实践过程中遇到过的一些实际问题与解决方案。本文大致分以下几部分内容:
为什么选择 React Native
React Native 的现状
Ctrip React Native
携程火车票的 RN 应用
踩过的坑及解决方案
各种问题及优化步骤
一、为什么携程火车票要选择 React Native作为目前携程 App 为数不多的主要以原生开发方式为主的 BU,我们也曾在 Native 跟 Hybrid 两种方案中纠结过。一方面,原生的交互性能跟用户体验都是最优的,Hybrid 则始终还未能突破性能瓶颈;但是另一方面,原生应用的更新又是一个很大的问题,尤其是 iOS 的 App Store,每次发布更新都需要漫长的审核周期,无法做到及时更新,并且上线之后的维护也非常麻烦。对于业务高速发展、更新比较频繁的火车票业务来说,携程 App 一个月左右一次的大版本发布已然无法满足需求。所以这个时候,基本兼顾到体验与更新两方面优势的 React Native 的出现,无疑非常值得我们一试。从性能体验来说,Native 最为优秀,RN 基本接近,Hybrid 则有些硬伤,因为 Hybrid 的 View 层实际上还是前端开发人员所熟知的 DOM,而 React Native 则是以 Virtual DOM 的方式操作跟渲染相应平台的 UI 组件,所以性能要高于 Hybrid 而不逊色于原生;而更新复杂度上,Hybrid 跟 RN 都比较低,可以进行无需发版的 bundle 包更新,而 Native 则受限于应用商店的发布更新,复杂度最高;另外,从开发成本角度来看,Native 开发周期相对较长,编译调试也相对复杂,并且不能跨平台,Android 跟 iOS 需要维护两套完全不同的代码,RN 虽然比 Hybrid 成本稍高,但是远小于 Native,可以做到大部分代码的跨平台复用。二、React Native 的现状从这上述的几个方面来看,RN 的各项表现都是非常优秀的。然而,目前 React Native 仍以每两周一个版本的更新频率快速变化中,到现在最新的 0.35,仍旧是以零点几的版本在定义,还不能算是一个完全成熟的框架,所以在实际应用过程中还有许多坑要趟。比如版本升级问题,有时候可以平滑过渡,有时候就没那么轻松了。就拿我们年初实践的汽车票独立版来说,1 月份刚开始使用的时候,React Native 刚开源 Android 版本不久,在 Android 上的兼容性还不是很乐观,所以只在 iOS 上做了尝试,我们最开始使用的是 0.18 的 RN 版本,从如何集成到现有的 App 里、怎么打全量包或增量包、以及 bundle 包的发布等等问题,当时都是组里的小伙伴跟 iOS 开发小伙伴自己一步步摸索过来的,但是在 RN 的快速更迭下,等尝试升级到零点二几的RN版本时,一言不合某些组件跟 API 就不能用了。三、Ctrip React Native在携程基础团队向我们各个业务团队提出 Ctrip React Native 的支持时,我们几乎毫不犹豫就确定要在携程火车票里接入了,算是公司里 RN 应用比较早的 BU,也是第一个使用 RN 进行完整的购票下单流程的 BU。CRN 抹平了很多 iOS 跟 Android 组件的差距,比如 DatePicker、SegmentedControl,提供各种携程风格的组件和API,如 HeaderView、HtmlText、Storage、Fetch 等,使业务开发更专注于业务逻辑,不需要过多费精力在跨平台的代码实现上,也避免了各个 BU 的大量重复劳动;此外,CRN 对首屏渲染速度的提升,使 iOS 能在 200ms,Android 在 400ms 左右完成首屏渲染,以及对 ListView 的优化等都让 React Native 向 Native 靠近了一大步;另外,包括对打包拆包、业务代码发布及诸多工具比如 crn-cli 的提供和支持,都让业务方更无压力的接入 RN 应用。可以说,CRN 实现了把 React Native 作为一个纯技术框架像业务框架的转变。四、携程火车票的React Native应用携程 App 从 6.17 版本开始有业务试用 React Native,到 6.18 也只有 2 个 BU 尝试了 3 个 RN 页面的上线。到 6.19 版本,火车票 RN 强势加盟,7 个 BU,21 个 RN 页面里,单火车票一家就占了 8 个。再到 6.20 版本,在 CRN 的加持下,15个 BU 接入了 50+ 的页面,我们到达了 17 个,包括云抢票、改签抢票、接送站等业务,并且这些页面大部分都比较重要且有较为复杂的交互逻辑。五、踩过的坑及解决方案从一个火车票购票流程里粗略提取一下具体实现就有如下几点:
从几千个城市站点里选择目标城市;
在各种车次、座席、出发时间里筛选出合适的车次;
乘客信息的填写或者选择。
首先,购票流程第一步的站点选择,就遇到了一个大坑:大家应该知道全国大大小小的火车站至少有数千个。而接触过RN社区的小伙伴应该知道,React Native 的 GitHub 上有条臭名昭著的 issue,官方至今还没有完美的解决方案,就是 ListView 的性能问题。RN 自带的 ListView 是没有回收机制的,这样就使得 RN 在加载较多个数据的列表,App 会非常吃内存。我们一开始也尝试用自带的 ListView 来加载城市站点列表,几千条纯 Text 渲染下来时感觉还能勉强接受,但在加上了 View 布局、Touchable 事件之后,当时连在 iPhone 6,iOS 8.2 的系统下也非常吃力,越滑越卡,甚至在较低配置的设备上还出现卡死甚至 Crash 的现象。实际上,城市站点选择是一个变更频率很低但是使用频率很高的页面,考虑到 RN ListView 的优化空间有限,一旦出现卡死,对火车票来说,结果基本是灾难性的,所以我们最终选择了复用原生已有的城市选择页面,由封装成一个 station component 供 RN 使用。现在我们考虑下另外一个重要场景的实现,从账号里的常用乘客列表里勾选乘车人,同样作为一个列表,是不是也可以像站点列表一样复用 native 组件呢?我们也确实这样考虑过,看起来好像省时省力、皆大欢喜。但其实正常情况下,个人账号里的乘客数量远小于上千条的站点数据,RN 实现是可以负荷下来的。而且 station component 只需要传给 Native 一个已选的车站,然后 Native 组件里操作完成后返回一个重新选择的车站就可以了。乘客列表并非简单的单选,而且除了点选,还涉及到新增编辑等等更多的交互。由 Native 封装的话,涉及到的参数传值未免太多。那么是不是可以跳转到一个新的页面,加载跟渲染数量较少的乘客列表比较方便实现呢?从产品层面来说,火车票购买作为一个购票流程,每多跳转一个页面就有可能损失一部分转化率,所以为了尽可能减少页面的跳转,我们采用了浮层形式在订单填写页面里进行乘车人的选择。然后问题又来了,在浮层弹出的动画过程中加载并渲染乘客列表,很容易出现失帧卡顿的现象。如何解决?我们是这样考虑的,列表的加载并不是非要在浮层弹出的同时进行的,在进到订单填写页时就可以预先加载好乘客列表数据,而只在浮层里做渲染即可。而且可以在不影响用户视觉体验的前提下,增加一些短时间的延迟。先完成浮层的弹出动画,使用 RN InteractionManager 的 runAfterInteractions 等动画结束之后渲染数据,并且设置了 ListView 的 initialListSize 跟 pageSize 均为 1 来逐条进行渲染。前面也提到了,我们是希望尽可能减少页面跳转的,所以像车次类型、时间段筛选、座席选择以及前面提到的乘客选择,都是在各个页面采用浮层形式来实现的。但其实浮层涉及到的问题相当多,这里仍旧以乘客选择浮层为例。乘客浮层要实现的功能非常之多,首先,内部的列表是可以滑动的,上部分的阴影可以点击消散浮层,并且内部的 Item 又要响应各种点击操作。拿到这么一个复杂的需求,最开始的做法是先给整个 Modal 加个 TouchableHighlight 事件来处理消散动作,然后内层加上 TouchableWithoutFeedback 来避免触发外面的 hideModal 动作。然后再在每个 Item 上加各自响应的 Touchable 事件响应勾选或者取消。然而,各种 Touchable 事件嵌套之后,实际效果就不在预期范围内了:滑动内层列表的时候突然划不动,点击 Item 却没有反应等等,经过一番调试跟定位,终于确定,ScrollView 滑动过程中很容易触发到外层的 TouchableWithoutFeedback,所以也就 without feedback 了。问题一定位,解决方案自然也就出来了,Touchable 的过多嵌套导致了问题的产生,那么就应该重新进行层级的布局,避免这些不应该的嵌套,不在整个Modal上加hideModal事件,而是抽出与浮层同级的View来做消散动作,内层只专注于List的渲染跟Item的点击事件监听。六、各种问题及优化步骤除了上述那些复杂的需求导致的复杂实现,还遇到过很多比较 low 的问题。大多数时候如果知道了一些属性就完全可以避免很多问题的产生。不知道大家有没有遇到过 setState 方法刚设置完一个状态,取这个状态却发现没有生效的情况。这个异步方法让我写出过很多丑陋的 setTimeout 来尝试解决。结果查阅 React 文档后发现 setState 是有第二个参数的,这个参数就是设置完 state 之后需要立即调用的函数。另外,合理使用 key 属性跟各种React生命周期钩子函数,如 shouldComponentUpdate,可以优化很多性能问题。再比如长按累加累减这样的需求,单纯的 onPress 跟 onLongPress 是不能实现的,需要结合 delayLongPress 直接触发 onLongPress,并且在 onLongPress 里进行 setInterval 的递增或者递减操作,然后在 onPressOut 解除 press 动作的同时 clearInterval。伴随着业务的高速发展,逻辑越来越复杂,我们也逐渐发现了很多做得不好,值得优化的地方。像前面提到过的 View 层级过多导致的内存消耗、页面卡顿。页面逻辑复杂导致 state 设置过多,出现切换卡顿现象,并且状态管理越来越混乱。而且现在很容易出现单个页面动不动就一两千,甚至几千行代码,维护起来非常困难,还有很多重复的代码实现等等。这些问题我们也在考虑从很多方面优化,像布局上尽可能减少层级嵌套,尽可能抽取能够复用的组件,都是大家需要注意的点,状态管理上我们也在考虑如 Redux 等一些好的解决方案的引入。以上,希望能与大家共勉。了解最新移动开发相关信息和技术,请关注 mobilehub 公众微信号(ID: mobilehub)。

我要回帖

更多关于 为什么选择我们公司 的文章

 

随机推荐