京东618:ReactNative框架在京东无线端的实践

栏目: 服务器 · 发布时间: 7年前

内容简介:京东618:ReactNative框架在京东无线端的实践

一、诞生背景

1.无线开发的痛点

京东618:ReactNative框架在京东无线端的实践

React Native最近两三年之内整个框架在业界应该说是非常热门,很多团队、大公司都在做RN的一些研究开发工作。先一起回想下在React Native框架出现之前,互联网APP开发是一种什么样的模式。最初,大多数同学应该是用原生开发Android或者iOS,再加上HTML5内嵌的方式,即Web APP。之后又衍生出了Hybrid APP,基于PhoneGap/Cordova框架实现了WebView的能力强化。不知道大家在做这种开发的时候,有没有遇到过一些瓶颈或者一些痛点,反正我们的团队是遇到了很多。这里总结一下之前传统的方式有哪些问题。

第一,效率低下。因为无论是Android还是iOS,使用传统的原生开发都有一定的开发门槛。而且代码上不能复用,这意味着任何一个业务要在Android和iOS各做一次开发,测试和业务开发工作都不能复用。

第二,性能比较差。用传统的H5开发方式,受限于 WebView 容器的一些瓶颈,导致无论在页面加载还是用户体验上,相比原生应用有比较大的差距。

第三,灵活性不够。因为传统原生开发意味着任何改动都需要发版,在Android上因为像国内应用商店非常多,而且涉及到各种不同的渠道包,所以发版成本很大;在iOS则受限于苹果的审核机制。对我们来讲,任何这种线上问题处理起来都非常痛苦。

最后,接入困难。因为Android和iOS平台有差异,所以任何一种垂直业务接入APP的成本非常高,很多业务代码和业务流程并不能复用,造成业务团队的开发、接入成本非常高。

2.React Native登场

京东618:ReactNative框架在京东无线端的实践

说了这么多痛点,我们也在反思到底需要一种什么样的框架来解决这些问题。非常幸运,我们在2015年的时候注意到Facebook发布了非常具有颠覆性的RN框架。简单来说,这是一种跨平台的移动应用开发框架。在当时它非常有颠覆性,因为它最大的特点就是完全用JavaScript进行应用的开发,但是最终会渲染成原生的组件。对开发者来说,这意味着你拥有了Web开发的效率,同时兼顾了原生的性能。这对我们当时业务的吸引力非常大,这个框架一经推出,国内外很多公司都在用,像Facebook自己也在用。在国内,手机百度、手机QQ、京东APP也很早就进行了开发。RN对我们团队来讲都有哪些优点,或者说为什么要用它,这里大概总结了以下四个原因。

第一,学习成本低。因为它的开发基于JavaScript,JS语言本身在开发者当中有非常良好的群众基础,任何一个有经验的前端团队可以快速地上手RN开发。

第二,多端代码复用。因为所有的业务都用JavaScript开发完之后只有一份代码,然后通过编译打包机制直接部署到不同的平台,如Android、iOS甚至Windows平台。

第三,接近原生的性能。开发者使用JavaScript进行RN框架开发,开发完之后在再通过中间的虚拟DOM。这个实际上是它核心所在,传统的H5的应用是跑在Web View的容器当中,容器中需要维护一个真实的DOM,而真实的DOM上每一次操作都会有回流(reflow)和重绘(repaint),效率并不高。Facebook最有颠覆性的一点就是提出了一个虚拟DOM的概念,把整个DOM放在内存当中,然后通过高效的diff算法来计算比较哪些UI组件需要更新,最终只对这些需要更新的组件进行真实操作。经过测试,采用RN框架,无论是加载性能还是页面滑动性的用户体验上,都比原来H5的方式要好很多。

最后,社区活跃。除了Facebook之外,GitHub上有很多第三方的团队、个人、公司开发贡献了很多非常优秀的第三方组件,它的社区是非常健康、非常活跃的。

3.React Native的局限

京东618:ReactNative框架在京东无线端的实践

不过现实是残酷的,即便确定了用RN框架做业务开发,在实际的开发当中也发现了RN的一些不足。对我们的业务来讲,最不能接受的主要是以下四个方面。

第一点,RN框架原生并不支持Web端。这意味着如果一个业务需要同时上Android、iOS和H5页面的话,那除了用RN之外,还需要用传统的H5或用ReactJS框架再做一次开发,这样效率是非常低的。

第二点,RN框架官方并不支持热更新。虽然现在有很多第三方方案,比如微软的CodePush,但是官方并不原生支持热更新,而热更新对我们的业务来说也是非常重要。

第三点,Facebook给出的官方RN API不能完全满足业务快速的发展。它只给了一些很基础的API,但业务中经常会用到的一些多媒体,比如录音、录像、视频播放文件以及文件上传、压缩、加密等等,这些都没有提供。

最后,前面提到RN框架性能非常不错,比H5好很多。实际上经过真正的业务开发后,发现90%的场景下RN的性能非常棒,可以满足我们的业务需求;但是在另外的 10%的场景下,特别是一些交互非常复杂、页面非常复杂、需要频繁的更新、需要一些手势交互的场景,RN仍有些内存跟性能的瓶颈。

4.解决方案:JDReact三端融合平台

京东618:ReactNative框架在京东无线端的实践

既然RN有优点也有缺点,那怎么办?

我们的解决方案是 基于RN框架进行了深度定制和二次开发,逐步打造了符合京东业务的JDReact三端融合平台 ,主要的工作是以下四大方面:

第一,把RN的核心Base库拿来做裁剪和二次开发,把不需要的功能删减掉,把性能、兼容性、稳定性的问题修复,包括也支持了拆分打包。

第二,在后端搭建了一个功能支撑平台,帮RN框架增加了灰度更新升级、数据监控以及降级容灾功能,这些对业务来说是非常重要的。

第三,基于整个RN框架,结合京东的一些业务特点,封装了一套自己的业务组件,包括UI公共组件库。目的是为了让垂直业务开发者可以很快地使用框架进行业务开发,完全不用关心设计的样式跟交互,可以快速接入业务。

第四,打通Web端,实现了一套RN框架向ReactJS转换的工具。可以做到一次代码编写,直接部署到Android、iOS跟Web三端。

二、JDReact三端融合平台全解析

1.整体架构

下图所示就是整个JDReact三端融合平台的架构图。

京东618:ReactNative框架在京东无线端的实践

最下面是一个后端接入平台,包含刚刚提到的灰度更新、降级容灾、数据采集和持续集成,这些是由服务端提供的一套服务。中间这一层是提供给内部开发者的一套完整的SDK开发工具,里面除了一些API之外,也封装了大量的京东定制功能组件,包括UI公共组件。其中还有一块是Web转换工具,提供了一套RN转换的脚本。业务开发者完全不需要关注这些细节,只要关心他自己的业务逻辑,就可以直接开发出覆盖三端的应用。最上面的业务层就是京东APP所有使用三端融合平台开发的业务,这些都可以直接部署到Android、iOS和Web。

2.改进和优化实践

前面主要介绍了整体的平台架构,现在开始来分享一些干货,就是我们在开发过程当中团队遇到的RN的一些问题,包括如何改进跟优化的一些实践。我列了一些功能点跟大家一起分享。

功能裁剪

有同学抱怨过RN库太大了,所以拿到RN的第一件事就是裁剪。对Android平台来讲,除了把RN的基础库裁剪以外,很重要一点就是要把方法数减少。因为Android平台dex有方法数限制,一旦超过65K就需要拆分成多个dex,整个应用的安装跟加载都会有性能问题。所以,要对Android方法数进行严格控制,我们的做法就是根据业务情况,把一些用不到的组件方案中的功能组件删除。其中最重要改动就是把Android中support-v7和stetho库依赖给去掉,去掉之后不仅大小减小了很多,而且方法数减少了将近7000。除了移除这个功能库,很重要一点,因为不是一个全新的RN应用,需要跟现有的体量很大的APP做集成整合,所以尽量让一些依赖库复用主站中依赖库,比如fresco、okhttp等。一来缩减包的大小,二来避免包的冲突。但是主站中的版本很可能跟RN中引用的版本有差异,需要中间做一层适配层,把这些差异尽量抹平,保证这些功能和方法都能工作。

加载性能优化

虽然说RN框架号称比H5的加载性能快很多,但实际开发中发现在Android的一些低端机型上,加载速度还是达不到原生体验,极端情况下甚至会出现白屏。主要原因是业务jsbundle比较大,RN框架在加载jsbundle和通过JSCore解析jsbundle时耗时太长。当用户看到真正业务页面之前会出现长时间的空白页面。

当时提出了两个解决方案,第一种方式是实现一套预加载机制。预加载机制就是在用户真正进入业务之前,把jsbundle提前加载解析,提前把RootView生成。简单来说就是用空间换时间。但这样做并不是所有的业务都适合,因为会带来一些内存增长,所以一般在很核心很重要的业务采取预加载机制。第二种方式是修改了RN框架底层库,在RN框架开始加载jsbundle文件时,显示一个loading的进度提示用户正在做加载的动作。当JS文件加载并且解析渲染完成之后,把进度条去掉,最终被页面展现给用户。这样虽然等待时间并没有减少,但是用户体验会好很多,整体的时间从收到的反馈来看还是比H5要好很多,这是我们做的一个优化点。

内存优化

京东618:ReactNative框架在京东无线端的实践

我们还做了一件很重要的事情,就是内存优化。在RN框架开发中碰到的最大的坑就是内存这块,因为业务中会经常碰到ListView的使用,根据这些业务的需要,可能要加载很多页,两页、三页、甚至可能会无限加载。这种方式在早期的RN版本当中肯定会引起OOM(OutOfMemory)崩溃,原因是在RN的早期版本当中并没有对ListView做内存复用。这意味着ListView滚多少,图片都会在内存中,当页面加载地越多,出现OOM崩溃的几率也越大,这是一个非常不能接受的问题。

在RN的早期版本,我们团队在JS层实现了一套内存回收。它的原理跟原生当中的原理也差不多,就是当页面划出两个屏幕之后,会强制把图片和内容进行回收,用一个空白的View替换。当内容划到用户可见的屏幕范围之后,再把图片给加载出来,这也是原生常用的一种内存回收的方式。修改后的效果很好,无论页面加载再多,都不会出现卡顿和OOM崩溃。在RN的新版本(0.43之后),引入了一个新的FlatList组件。这个组件完全解决了ListView的内存回收问题。它的实现机制和我们的方案类似也是在JS层中做内存回收的动作。所以给大家建议,如果开发中碰到类似的问题,完全可以升级到最新的RN Base 0.43以上使用FlatList组件。如果版本比较低的话,那就需要自己实现这套机制。

第二个比较大的内存问题就是图片,iOS平台可能相对好一些,在Android问题会相对多一些。RN的底层图片框架库用的是Fresco,而我们主App中用的也是Fresco底层库,这里就会有些问题。第一个就是重复初始化,这也是当时业务开发当中碰到的问题。当主App中的Fresco进行初始化之后,如果RN中也进行一次初始化,实际上之前那部分内存并没有被释放,会出现内存泄漏。我们做了专门的检测,避免RN重复初始化的问题。第二个也是跟RN框架里面的实现有关系,因为它采用的图片编解码用的是ARGB_8888,这种方式支持Alpha通道。但实际上大部分情况下可以采用RGB_565编码,虽然丢失了Alpha通道,但是图片在内存当中的大小可以减少50%。不过有些业务可能也真的需要一些透明的背景,需要Alpha通道,所以也提供了一些API来针对特殊图片,让它采用ARGB_8888进行编解码。这样既解决内存问题,也满足了业务的需求。

最后一个经验就是在所有的RN页面退出之前,建议强制调用Fresco框架的clearMemoryCache方法,通知Fresco清除内存缓存。可以保证GC及时地把这些图片内存给回收掉,避免整个APP的内存占用过高,经过实践验证这也很有效地解决了内存问题。

拆分打包

京东618:ReactNative框架在京东无线端的实践

关于拆分包,因为目前我们采取的方式是每一个业务打成一个jsbundle文件,这意味着业务越多,jsbundle文件会越大。而这些jsbundle文件当中,业务的代码其实占比很小。百分之七八十都是Facebook提供的一些公共组件库。我们的做法是在编译打包之前,把这些公共组件库先抽取出来,放在一个common jsbundle里面,然后业务只保留业务相关的一些jsbundle文件。最终在真正的加载之前,做一个简单的合并动作,这样业务越多,这种优化的效果就越好,可以有效减缓jsbundle文件大小的增长速度。

性能优化

京东618:ReactNative框架在京东无线端的实践

除了内存之外,最关心的就是性能。前面也提了RN的性能其实比H5要好很多,可以满足我们90%的场景,但实际上还有10%的场景,RN做的并不是很好。主要也是因为整个RN的机制,它虽然是最终渲染成原生的组件,但是UI的控制还是在JS中做的。受限于JS单线程一些限制,当有一些很复杂的交互、很复杂的手势或者快速的滑动,很有可能引起JS中的阻塞,造成动画的一些渲染的数据不能及时同步到原生当中,造成了整个页面的卡顿。

建议的方案有三种,第一个做RN的Base升级,把RN升级到最新的0.45,它会采用了一个新的叫Yoga的引擎。这种引擎是完全用native实现的,可以把大部分的动画渲染和交互放在原生的线程中做。经过测试,采用了Yoga引擎,整体的渲染性能可以提升30%以上。

第二种方式,有一些非常复杂的一些交互,比如左右滑动结合上下滑动一些手势,如果用单纯用RN做,很容易碰到一些手势冲突的问题。所以把这种组件原生化,完全用原生实现,所有的交互跟手势控制全在原生做。这样做就可以达到非常完美的性能,但同时也需要原生开发团队介入。

最后一个经验就是尽量使用Animated这种动画类,减少JS控制的UI数据同步,避免JS线程阻塞。

版本检测

京东618:ReactNative框架在京东无线端的实践

另外在jsbundle文件当中增加了一个version文件,解决版本冲突检测。因为要支持线上更新,就意味着需要把每一个业务jsbundle文件做一套完善的版本控制。需要知道当前这个jsbundle文件的版本号是多少,可以跑在哪个客户端的版本当中,可以支持的这它的RN底层库是多少。这些信息都会记录下来,然后在每一次的升级之前做版本检测。这可以有效地避免线上不同客户端和不同RN版本之间的版本冲突问题,可以支持线上的灰度升级。

兼容检测

京东618:ReactNative框架在京东无线端的实践

RN其实有最低版本支持,像它的早期版本在Android是支持API 16以上,iOS是iOS7以上。其实我们的主APP要支持的版本会比他更低一些,所以需要在主APP中做一些保护和判断,一旦检测到用户的版本不支持RN,就需要做一些降级处理,比如说把入口关闭或者跳转M页。这样最大的程度避免不支持RN版本的用户出现崩溃的情况。RN其实可以支持x86芯片,但是考虑到如果要支持x86的话,需要增加一套基于x86的so文件,会对包大小有影响,所以对所有的x86做了降级。

原生能力扩展

京东618:ReactNative框架在京东无线端的实践

前面刚才也提到了我们的业务非常多样,很多的能力RN并不支持。所以基于RN框架我们扩展很多业务上用到的原生组件,比如做了整个多媒体的视频播放、视频录制、音频播放、音频录制等组件,还有一些文件上传、语音识别等。在RN提供了这套JS的接口,给垂直业务团队快速开发和使用。

3.通用组件库封装

京东618:ReactNative框架在京东无线端的实践

我们也结合自己的业务做了一套通用组件库的封装,例如京东当中的用户登录、购物车、收银台等等业务,在RN中做了一套组件的封装。把所有的接口都提供了JS的API,样式和交互像常用的下拉刷新、对话框、按钮等等,也提供了一套通用的样式组件给开发者。在做业务开发的时候,完全不需要关心这些样式怎么画、颜色怎么搭配,只需要关注业务逻辑。剩下的事情由框架做,这可以提升整个业务开发的效率。

4.三端融合

京东618:ReactNative框架在京东无线端的实践

刚才前面提到了很重要的一项工作就是克服了RN不支持Web端的问题。我们做了一套Web转换的工具,打通了三端。其实在业内三端融合也有广泛的研究,方案主要有三种。

第一种方式,就是在RN跟ReactJS之上再封装一套轻量的跨平台的抽象层,像微软发布的ReactXP就类似于这样的架构。使用这种架构,意味着所有API、类、组件都不能用RN API,必须要用新的定义的接口,而且目前API支持也不是太多,还在完善中,所以没有采用这种方式。

第二种就是ReactJS做开发,之后通过 工具 转换成RN,这种方案适合于比较偏重H5业务的一些团队,因为他优先需要上的是H5页面,用户体验比较偏重H5。通过工具向RN转换其实是个有损转换,因为RN支持的样式实际比CSS样式少。从ReactJS向RN转换的话,可能会丢掉一些属性和布局。

第三种方案就是先用RN做开发,开发完之后再通过WebPack工具向ReactJS进行转换。这种方式的好处是可以优先保证RN中的体验,而且RN的样式支持是CSS的一个子集,这意味着从RN向ReactJS转换不会丢失功能和属性,所以业内更多的方案也是采用这种方式。GitHub上有一些类似的开源框架。但它们支持的组件并不是太全,不能完全覆盖我们的业务,所以我们自己实现了一套。包括之前说的所有的原生组件,它只有原生部分,我们也增加了JS部分的实现,使我们的框架可以完整、功能完全没有丢失地转化为Web页面。

5.灰度更新

京东618:ReactNative框架在京东无线端的实践

下面简单介绍我们后端的接入平台在服务端增加的灰度更新控制。发版之后,如果需要做一些RN组件更新,可以通过后台的更新服务器做一次支持这种灰度的更新。它大概的流程就是首先由APP端发起更新的请求,发送到路由控制,路由控制负责控制用户是不是在灰度比例范围之内。如果符合灰度策略,把这个请求转到服务端处理,服务端根据客户端上报的jsbundle文件的版本跟服务端部署的版本做一次比较,看有没有适合这个业务的新版本。如果有,把这个升级的版本号以及下发地址回传给客户端,客户端会直接根据下发的下载地址从云存储上下载升级包,完成整个升级过程。因为用户量比较大,所以每一次更新一定要有一个灰度策略,根据灰度比例逐渐放到全网,这是非常重要的。

6.降级容灾

京东618:ReactNative框架在京东无线端的实践

我们把降级容灾定义为两种:一种叫被动降级,一种叫主动降级。所谓的被动降级是指客户端确实不支持RN框架,每次加载RN框架都会出现问题,那必须要进行被动的降级,跳转到对应的H5页面,使得对业务的影响降到最低。这种降级逻辑是在客户端当中做处理的,就是前面介绍的兼容性检测。第二种是主动降级,很可能在业务开发的时候会发现一些上游的接口出现了问题,导致客户端中的某项业务不能正确地运行,这个时候就需要由服务端控制对这项业务进行精准的降级。我们会支持多个维度灵活的配置,可以根据客户端的版本号、客户端的型号,配置灰度比例、白名单,精准地对某些用户的某些业务或者某些地区的某些用户进行降级,减少业务上的损失。在一些非常大的促销的时候,像双11、618这种峰值非常高的时候,可能会采用这种方式。

7.持续集成

京东618:ReactNative框架在京东无线端的实践

这主要是我们内部的一个开发模型,把所有RN的基础库,包括自己提供的一些公共组件库、公共UI组件库都部署在内部的NPM Server上。每一个接入的业务开发者、每个业务都会有一个独立的GIT。因为在我们内部,其实业务开发团队可能会很多,我们的团队是负责维护框架,而业务开发团队各个部门各个地区都会有,他们会有申请自己独立的GIT,然后从NPM Server上下载最新的SDK包进行业务开发。业务开发完成调试之后,会通过CI打包平台发起打包命令,然后触发Jenkins当中的job,从对应的业务的规则拉取代码,进行编译打包。编译打包成功之后,把它部署到对应的Android或者iOS客户端版本当中进行整个发布。这种方式对业务开发者来说,最大的好处就是打包编译完全是脚本自动化,不需要获取客户端的源码就可以做到这个业务的开发和上线。

8.数据监控

京东618:ReactNative框架在京东无线端的实践

JDReact三端融合平台我们也做了一套非常完善的数据监控中心。因为需要知道所有RN页面启动的时间、加载页面的时间、服务端返回的响应时间、界面渲染的时间。需要把这些APM数据上报,通过上报的数据分析,不断地优化性能。第二,也会把业务开发当中碰到的一些异常日志进行上报,可以帮助我们快速的定位问题,发现问题并部署相应的升级包。第三,因为灰度升级更新这个机制,需要有数据埋点来统计升级的成功率。有多少的用户真的是发布升级之后可以成功升级到这个版本。最后,也会针对DAU、UV、PV等基础数据做统计,这个主要也是帮业务方搜集一些运营数据,好做业务上的决策。

三、总结

这个框架推出有一年多的时间,到目前为止,京东APP当中已经有20多个业务正在使用这套框架,其中也有一些比较重要和常用的业务。我们整个平台也经历了去年的双11、618,今年即将到来的618,我们也会做更多的后台保障,在稳定性,包括降级上做一些处理,确保业务能够正常地推进。未来也希望能够不断的完善这个平台,不光是京东内部在用,一些很好的组件框架也可以开放出来,跟大家一起学习进步。

作者简介

沈晨,京东商城专家架构师、JDReact三端融合平台负责人。

感谢 徐川 ,木环对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First HTML and CSS

Head First HTML and CSS

Elisabeth Robson、Eric Freeman / O'Reilly Media / 2012-9-8 / USD 39.99

Tired of reading HTML books that only make sense after you're an expert? Then it's about time you picked up Head First HTML and really learned HTML. You want to learn HTML so you can finally create th......一起来看看 《Head First HTML and CSS》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码