【前端】饿了么在移动O2O应用React Native的技术实践
作者: 戚岩 饿了么资深前端开发工程师
2016-03-22 14:42:13
【本文系ITA1024原创首发,转载或节选内容前需获授权(授权后一周以后可以转载),且注明来源:ITA1024互联网技术开放日实录。欢迎更多互联网研发团队与ITA展开内容合作,欢迎个人技术原创投稿。投稿及授权联系邮箱:openday@ita1024.com,联盟微信:ita1024k,官网:www.ita1024.com】 2016年3月19日,由互联网技术联盟(ITA)举办的1024前端技术峰会,在中关村WEPAC盛大举办! 400+位经验丰富的前端工程师共同参与,是一场业内最顶级讲师阵容的前端技术峰会,而且,这是一场不落幕的峰会,因为3月开始的每一周,都有线上的分会场如期分享着各个一线互联网公司在前端技术方面的最佳实践。 饿了么资深前端开发工程师戚岩分享的主题是:React Native在移动O2O应用中的实践。 如下是3.19ITA1024互联网技术开放日戚岩分享实录。 大家好,我叫戚岩,很高兴收到互联网技术联盟(ITA)的邀请,在这里跟大家交流前端开发,我今天分享的主题是React Native,主要是基于我们之前做的饿了么商家招聘配送员和兼职平台的IOS应用的经验,目的是帮助对React Native感兴趣的同学了解React Native目前发展的情况,还有你如果想选择React Native进行移动端开发,这个过程中将会遇到哪些坑,迈过哪些坎儿。 之前用React Native开发了一款IOS移动应用,饿了么北京团队是不区分终端开发和前端开发,对React Native感兴趣,或者安卓、IOS都可以向我们靠拢。 第一个问题是我一直比较关心的问题,脸谱为什么要开发React Native? 我认为对于一个框架持续的发展,如果没有一个生长环境为它服务的话,是不能够持续发展的。其实这个问题也间接能帮我们看清楚什么类型的APP适用于React Native进行开发。其实React Native的出现并不是想替代OC,接管所有的IOS开发,比如脸谱的一款交互非常复杂的产品,就不适用于React Native开发。RN适用于业务复杂型,而不是页面复杂型。 在加入饿了么之前我一直在很多公司做广告方面的业务,广告就是一个典型的业务复杂型,其实在广告系统中进行js开发,写的是很复杂的业务逻辑,不单单像普通用户产品一样只有B端、C端,最多有管理端的比较轻量的配置。广告系统中有客服、销售、财务等等角色,会有很多内部后台的系统,其实这个进行同一种语言开发是可以大量互用代码的,如果不这样面临很困难的局面,用各种语言写的这套业务逻辑,后续想更新维护的时候,要把所有人叫到场,然后大家同步改一个东西,IOS页面限制可能会让其它端跟它很难重合,我自己觉得脸谱可能有这方面的痛点,然后开发了这样一个基于js的框架来完成各种手机APP的开发。 饿了么为什么选用React Native? 因为饿了么北京前端团队成立不久,开始我们没有React Native工程师,这也是对很多小公司,或者是一些大公司刚成立时,都会面临的常见问题。因为我们都是写Web前端工程师,对React Native感兴趣,尝试写了一个APP,实际看起来效果还是非常不错的,所以我们放弃了招聘原生IOS工程师,选择了React Native。 使用React Native进行开发的优势 这里有一个例子,业务中一个场景显示时间,这是很多应用中遇到的问题,时间显示的规则是今天发生的只显示小时分,如果今年就只显示月日小时分,如果今年以前就完整显示。做js开发会用到很多第三方npm库,日期我们也用了著名的库叫moment,我认为如果H5或者内部管理系统,或者是安卓、IOS都采用一种语言开发的话,带来最大好处是可以共享。代码角度我更看重怎么样互用组件。 做过IOS、安卓开发的人,估计他们会有各自的通用日期组件,如果你想用原生语言写,其实学一门语言非常快,但是要掌握它背后很多可用的开源组件框架,其实这个切换成本非常高,所以我认为用统一语言做移动端开发是有好处的。 用React Native进行IOS开发的优势 IOS有版本审核的问题,通常最快一个版本需要两周时间。React Native开发出来的APP会分成两部分,主要进行页面渲染的由React写的js代码打包成一个文件,目前有以下几种开放的平台,我们现在项目中使用的是codepush,apphub也用过,是收费版本,对使用人数、日活、用户量和打包文件大小都有限制,限制是比较多的。 最后exponent实现了作为ipe的功能,可以在XPD上面进行React Native开发。还有就是A/B test,小流量开发是Web开发经常用的手段。客户端开发更新版本的自身的问题,如果之前不用React Native一些可以发布的框架,进行A/B test只能在客户端写死一个算法,这样是不能够远程控制的。 React Native目前的一些问题 React Native目前是一个还在beta版本,对自己的版本定义还在零点几的水平,我们这个APP开发经历版本是0.14到0.22,基本上在每两周发布一个版本,更新速度非常之快,在这样的情况下难免会存在这样的问题,就是会有很多已知的性能问题不能改善,只能改善一些比较重要的问题。 用过React 开发Web的同学知道,现在基本常用的移动端组件都已经能够找到比较好的开源版本来使用了。其实移动开发跟PC不太一样的是,组件并没有那么多,我们发现自己写起来也不是很困难,因为受制于屏幕大小的影响,组件并没有特别复杂。 最后提到一点就是,文档已经完全跟不上版本升级的速度,升级过程中没有办法同步维护文档,以至于很多时候我们进行React Native开发不得不看它的源码,才能知道新开放的是什么情况。
React Native工作的基本原理 React Native是一个集成的作品,在此之前脸谱已经在oct端做了页面布局,可能在React这端有自己的结构,切换到React Native就做了接口的调用,实现的是RCT bridge,通过暴露给js的方法,分发到N Method当中,最后跑到手机上都是由原生代码实现的交互。
下面就是React Native的一些组件,对应的是原生IOS中UIP的组建,因为它虽然实现了一套类似于CSS的页面布局方式,但是这种方式其实并不是真正的CSS,它其实还是一种通过组件参数声明方式进行的设置,所以它实现了一套基于模型布局的方式,每一个样式组件都绑定到每一个标签上的,不能像HTML的CSS可以把任何属性写到任何DOM结构上。 接下来具体介绍一下我们在这款APP开发中需要开发的一些,对其它APP也会同样适用的组件和他们的使用情况。这个APP是帮饿了么商家招聘配送员、兼职的平台,初期我们能用React Native进行,刚开始算是试验形式的,取决于在业务上使用场景主要以安卓为主,因为它的受众安卓用户为主,项目临启动期间引入的也只有饿了么商家兼职任务,但是未来可能会考虑放入更多兼职的时候,IOS的用户也会越来越多起来。 Navigator是移动开发中最重要的组件,它在React Native有两个组件,我们初期想选用安卓和IOS通用的组件,但是发现它有动画效果不流畅,它在跨屏时,会出现之前页面缩小的转换,这在操作的时候会感觉页面卡。基于这个特点我们就放弃了Navigator,转用Navigator IOS。 Navigator IOS有好的一面,也有不好的一面。API比并没有Navigator丰富,Navigator IOS是因为大家发现Navigator不好用,模仿它的API写的,但是并没有完整实现它的所有功能,导致我们使用中发现它并不支持从底部向上滑出新的页面。这个时候我们就在所有需要底部滑出的页面上,又进行了其它的路由操作,分装了新的路由。 Navigator实现的路由是通过TOP和PUSH方式,从根目录,初始页面开始,可以路由出很多条分支,整体是树形结构。可以实现从一个节点一直跨级PUSH,它只有在叶子节点的时候才能够实现不用PUSH,而在树中间节点时不能被替换。 我们分装从下往上实现的Navigator,用动画实现了从右侧向左侧滑入的效果,然后数据结构上采用的是双向链表的结构,只记录了之前之后页面是什么样的关系,实现了相对简单的效果。
在进行列表选择的时候会有屏幕向上放滑下来的筛选条件,最开始的时候我们选用了React Native Animation,一步加载很多的时候会发现动画效果比较卡,卡的话还不如用H5实现。 还有一个Layout Animation,只能实现官方文档描述的实现一次性的动画,所谓一次性动画就是指动画中途不会被取消掉,不会进行连续不同的变化,对于展示型的APP,大多数需求都用LA是可以实现的,都会有这三种配置进行动画的过程转换。
下一个进行移动开发都会遇到的组件叫PullToRefresh,升级到0.18版之后就用了自带的效果。但是只有PullToRefresh还达不到我们需求,通常这样一个列表会要求下拉翻页,上拉刷新,还有查询结构的展示。这里面主要讲的是ActivityIndicatorIOS,根据业务情况不同会让它有选择展示的位置,比如这个地方如果是上拉刷新,在上拉的时候已经出现了Indicator,所以就不能展示。 接下来是前端开发非常熟悉的弹窗,React Native自带了Modal,它本身也是支持动画,可以从底部滑入,没登录想报名或者需要登录的时候,会从底部滑上来,它是全屏覆盖的,没有办法控制弹窗大小和位置,不能满足所有APP需求,然后我们分装了基于React Native的Modal,可以自定于弹窗内容是什么,位置在哪里。
这张图也是一个IOS开发常见的东西,IOS键盘上并没有收回的按纽,我们很多页面进行操作需要收起键盘,或者是一些提示信息如果没有键盘会在页面下面展示,但是有了键盘展示到下面会被盖住,所以需要有侦听键盘出现和收回的事件。我们重置一下提示的位置,还有就是React Native自己的组件,它在React Native点了其中一个,其它都不会触发。 只有点了另一个触发,才会有反应。最开始试图解决这个问题,用了Scrollvicw,用的时候发现并不是很完美,因为用它会失去焦点,第一次触发屏幕会失去焦点,收起键盘,但是并不能够点击一次就切换到下一个输入项。所以最后经过各种试验,找到了一个方法,用React Native提供的组件把整个弹性的容器包装起来,然后主动收起键盘,也不会影响输入项目。 下一个就是进行React Native开发和Web开发最主要的,也就是能做到更精确的操作,比如Swipeout,原理就是基于React Native API分装的Panresponder,事件周期跟H5的区别就是处理响应更丰富。在H5中只有几种方法,但在React Native是基于原生分装,可以精确显示到触发想要操作容器的时候,通过触摸触发,也可以通过滑动触发,触发之后立刻显示,拖拽就是这样的过程。 其它几个API实现的功能,在React Native中,同时有onrespondre工作,前一个panresponder运行周期中没有办法执行后一个。但是如果可以被中断,就会执行REJECT方法,另一个就可以进行新的执行。这还不足以让它跟H5产生多大的差别,它相比H5还有一点,除了返回事件,还会增加额外几个属性,我们在Web中没有见过的,比如X轴到Y轴移动的距离、X轴到Y轴的速度,这些可以帮助我们做更复杂的手势操作。 项目中的Web依赖 这是很多同类型应用都会面临的问题,当我们不想写原生OS代码的情况下,怎么能让我们APP实现绝大部分的需求。当然了,React Native只是一个容器,或者只实现了桥接的方式,我们可以实现拓展的React Native,实现各种原生功能,只要你想暴露给js都可以,或者你想原生代码在APP里实现也可以,我这里介绍一下我们项目中的Web依赖。
首先codepush,托管文件的单独云平台,这是微软开发的,未来也不排除我们会自己开发代码托管平台。code push可以把单独文件发到一个环境中,还有同步不同版本,支持回退任意版本。可以控制最小化轮巡的时间,自定义检查一遍有没有新的包,然后可以设置,在这次使用中,进行应用内更新你的单独文件,还是用户下一次打开APP更新,通常一般会做下一次打开更新。
我们用的第三方开源组件进行图片上传,这个APP对图片上传需求量不是特别大,只是用户头像和身份证校验,所以用了IMAGEPICKER,类似于原生,会弹出摄像头拍照或者读本地相册,授权就可以进行了。 比较重要的配置参数是做文件大小的限制,还有质量度的限制。但是这个东西不够灵活的一点在于它没有办法根据你的图片大小本身做剪裁,比如传了本来已经很小的图,不需要再做裁剪,但是用了这个只能一刀切。 我们需要重新开发,自定义根据图片文件的大小进行重新的配置。另一个是之前用到的DashBorder,0.18版React Native已经提供支持了。
O2O是基于地理位置的服务,所以用了第三方Location和geocoder,它还实现了很多跟HTML一样的东西,还是比较强大的。但是这个东西还不能满足需求,因为我们要把经纬度转成具体的城市或者具体的信息,需要用第三方的插件。 除此之外,我们并不能满足于在程序使用期间获取地理位置的需求,我们用了一个服务在后台拿到用户授权,目的是兼职在进行服务的时候,我们要进行定时向后端上报地理位置。 这是更加常见的需求,我们进行移动端开发都会用到开放第三方ID登录或者分享,初期用了openshare。但是微信和QQ分享的时候实现是有差别的,微信要两次才能拿到用户ID。之所以这个插件觉得不够用的原因在于用户手机上没有安装微信和QQ的时候,这个插件就不能够触发,后来我们换了一个更加好的share SDK,实现的如果没有安装微信,就第三方网页进行登录。
后面是接入分析统计叫talkingdata,它只提供了IOSICDK,其实对于React开发,自己身份周期是比较固定的,要检查配置是比较容易的。还有一些点击操作可以检测事件。统计可能是移动开发必须要接触的一个东西,我们目前是用了这个服务的。
Redux,它最近一年非常火,是基于Flux原理实现的简化版,对于复杂业务系统的前端开发来说,直接修改modle,业务复杂方案是难以维护的。演进到REDUX模式的时候,认为已经是必备的方式了,它更简单的一点是只有单一的STORE,只通过命令空间区分。
使用React Native时遇到的一些坑 首先是设备适配问题,浏览器适配,是我们一直要面对的事情。进行IOS开发,感觉这个问题会变得少一点,因为都是在IOS系统中。但是,第一个问题就是我们在IOS5中测出来的。实际上IOS8解析ES6语法的时候是分步支持的,所以我们之前用for…of做循环出了问题。 React Native的开发过程是跟Web开发过程相近的,也可以断点,报错信息也会比较固定。但是我们发现进行IOS开发js出错是很难解决的。比如会引起程序崩溃,我们也是用了IOS一个例子去看,出现了这个异常。React Native开发之所以能够比IOS原生开发提升效率,很大的原因在于出错更容易定位问题,IOS之前遇到问题会花了很长时间才找到问题的原因是什么。 再比如6Plus遇到的边框问题,像素密度不同造成的,React Native提供的像素比例API,可以根据不同的设备取不同的值,按照我们惯性思维可以定义所有设备,都表现一致,但实际在移动开发中并不是这样的。
在遇到的问题中,耗费时间比较多的是升级React Native版本。之前我们开发中经历了0.14到0.22,并没有每个版本都升级,但是会有很多时候用到新版本的特性,不得不进行升级。 升级的过程中每次都比较痛苦,经常跑不起来,原因可能也比较复杂,有可能跟nodenpi环境有关系,React Native自己的版本也不够稳定,之前遇到两个比较难解决的React Native自己依赖的包,支持是不一样的。还有之前遇到过用page进行搭配的时候,在模拟器上打包不成功,要手工改代码,而且如果升级之后,重新用appstore每次要手工改代码。 还有listview,体现在如果不进行初始化配置设置的话,会优先只展示两条,会看到数据是一点一点累计起来的,解决这个问题时,设置了一个比较大的page size,令它看起来是一次响应的。
在这张PPT上是接下来要做的事情,分析这一项现在并不确定,我们可能会接入饿了么公司内部的统计平台,可能能实现更可定制化的统计,是不是能够提供React Native的版本还不太确定,所以这个事情是待定的。 关于React Native FETCH,如果想要在这个做TIMEOUT,目前需要管理每一个请求。未来可能会做小流量的测试,还有可能通过位置接入高德地图,我们现在有一个产品需求,是要在APP中显示公交线路,这是用React Native地图API不能满足的公交查询功能,可能会需要引入高德地图,当然这个没有想好是H5,是Web6,还是原生方式,目前还在进一步的讨论中希望以后有机会继续跟大家分享。 谢谢。
————Q&A———— 提问:分享中提到code是做什么用的?
戚岩:不只做代码的托管,还能提供IDE,进行IOS开发的时候CODE非常重,提供了写代码的环境,我们没有用。
提问:LISTVIEW第二个说到pagesize,IOS开发刷新肯定数据量会很大,这种怎么做?
戚岩:分装,React Native包装到了6里面,多数地方用了分页方式加载数据,只有少数不确定,没有分页接口用的。
提问:现在还是没有用到?
戚岩:是。
提问:第三个问题发包的时候,平常React Native会影响多大?
戚岩:我们现在包不到1兆,几百K。
提问:关于安全性问题,我们写的代码构造界面代码都在里面,再比如写业务逻辑,采用第三方登录,如果全部用React Native写的话,安全性问题怎么解决?
戚岩:关于渲染页面js代码没有必要刻意隐藏,我们做Web前端也遇到这个问题,并不是什么需要不对外公开的东西。关于你说的React Native开发好处就是可以放到OC端,不在js那端做。我觉得一般的东西并不重要,因为我们这个APP也做了安全性的东西,最新版本做了银行卡绑定提现,做了rsa,传统Web方式做事情就可以了,没有体现出跟Web不同的点,现在反而多了一种选择,可以暴露的放在js里做。每次只询问有没有新的包,并不会下载,如果包一直没有变,是不会下载,包是存在本地。
提问:现在React Native适合开发哪些应用,您前面讲的是业务复杂型,能更详细一些吗?
戚岩:不知道你做没做过业务复杂型应用,我刚开始举的例子,广告系统这样的应用,前端开发复杂度主要体现在,广告系统是类似于管理平台,页面之间交互比较类似,但是通过广告主余额,投放广告地域,时段,进行复杂的操作都是业务代码,跟应用层并没有直观关系。
提问:在现有APP上用React Native来开发合适吗?
戚岩:饿了么我们现在了解情况就是有配送员用的两个页面,一个是等级,一个是通知,用的React Native,我了解之前也听过一些人分享,可能都只是对已经存在的项目,在部分页面采用,因为我们这个项目是全新的项目,所以才能整体使用,整体和部分都是可以的,可选的。 |
|