携程网框架研发部研发高级经理魏晓军于应用性能管理大讲堂年终盛典发表了题为《React Native 性能优化拆包技术及其实现原理》的演讲。
以下为演讲实录:
先简单自我介绍一下,我叫魏晓军,来自携程框架研发部,目前主要是做前端,以及一些开发、优化方面的工作,今天我带来的主题其实是一个简单的功能,可能是React Native开发里面遇到最普通的问题,今天只是提供一些解决方案,不过我今天分享的是我们内部根据这个原理去做的。
我先给大家回顾一下RN的历史,React Native可以说是Facebook在2015年的时候推出了Native上的框架,RN为什么这么火,首先是因为它解决了一个痛点,以前在开发效率上来说Native是比较慢的,后来大家想到H5,最后再到Hybird。Hybird是比较流行的移动端开发技术,但是也会带来一些问题,所以说Facebook推出的React Native的时候,大家发现React Native开发效率上和Hybird差不多,但是性能上是优势很大的。再一个是移动开发的时候的动态性能问题,我们常规像iOS开发,APPStore审核要一两周的时间,会遇到Bug不能及时修复的问题,所以React Native出来以后有一个特点,允许我们用JavaScript去开发Native,这一点又引起了业内关注。虽然它也有一些竞争对手,但是目前从生态圈来说RN是最大的。但它也有一些不足的地方,因为国外像Facebook很少会考虑H5的东西。React Native先出了iOS版本,然后出了Android版本。两个版本之间存在很多的差异性,甚至有好多组件都会带有平台的后缀,这使得开发人员必须要为这两个平台写不同的代码,不光是H5这一块,我们这一块是开发了Moles框架,我们简单介绍一下这个Moles框架。
mole [məʊl] 小鼹鼠,是种凿洞能力非常强的啮齿类动物。所以把框架称为之为Moles,也是寄希望我们的框架能像mole一样,能够打洞,能够打通Android、iOS、H5、SEO这几个平台。如果说当前移动端的三大痛点是:性能、动态性、多端适配的话。那么我认为React Native解决了性能、动态性,而我们Moles则解决了多端适配的问题。
Moles框架的一些简单的核心思路,我们还是依托于React Native作为Base部分,然后我们在上层封装了iOS和安卓的差异性的一些东西,然后将H5上的东西加进来,所以MOLES框架做出来以后相当于一个解决方案。除了兼容性方面还提供了一套管理工具,包括发布,打包,再有一些生命周期的管理,其实我们开发React Native的话如果大家要在里面埋一些性能点的话是需要自己去包装的。这不是我今天讲的重点,我们先看一下它的组成。
组成里面第一点Moles Packer,一个轻量级的打包工具,再有包含Moles UI,Moles Web,兼容React Native组件的组件库,Moles UI是把RN那一套所有的组件在Web上实现了一遍,相当于我们在Native端有RN,在Web端也是有RN的,只是我将Native上的所有的RN的东西在Web上适配了一遍,这样你在Native上写的东西才可以在RN上跑起来。
Moles Web浏览器端的React Native代理,一些东西都可以在Web上跑起来,但也不是百分之百的适配,底层的一些东西是在Web上做不到的。再一个是项目管理工具,这一块也是有一个套件,今天主要是讲Moles Packer。
讲到Moles Packer我们先回顾一下React Native打包,如果你要开发自己的组件还是需要Native的,对于我们大部分人来说应该还是写JS代码,JS代码这一块常规打包将你的所有问题,包括静态资源都打出来,打在一块,这对于做一个简单的应用或者是Demo完全不是问题,但是对于一个公司来说,以前是尝试做某一个页面试一下RN效果怎么样,现在是很多业务线都在介入了,不仅仅是简单的页面,所以问题也开始出现了,打完包文件特别大,现在有590K左右的样子,所以这个文件是特别大的,那就面临一个问题,它打包不让我们去拆包,没有拆包的功能。再一个因为我们是多业务线开发,不是一个团队,所以业务线之间可能有共用的关系,比如说可以定制化的UI组件,例如城市选择、日历等是可以公用的,但是如果走RN这一块打包的话就打在一块了,每个业务线会重复的打进去,所以我们需要将公用的东西抽出来,抽出来的话就是把它叫做是公包,业务自己逻辑是业务包。
这个拆包是原来分析过的,最开始是使用RN提供的打包工具Packager,RN提供的Packager,我深入研究了里面的代码发现是很复杂的,我们要进入到它里面然后对关键模块进行过滤,同时还要修改打出来的一些东西,这是常规打包,现在大家可以在谷歌搜索一下RN拆包,但是说的人比较少,不知道大家是没有遇到这个问题,还是RN现在没有在公司大规模的使用,之后RN大规模使用起来的话一定会遇到的这个问题的。
我们现在就讲一下Moles Packer能做些什么事情,首先在尺寸上只需把我们公用包做成公包,每个业务线自己的逻辑做逻辑包,有了公包以后有一个好处,在真正启动RN时候可以预先把公包执行,真正进入RN页面时候公包已经执行完了,只需要将业务包拉下来执行一遍就可以了,这样的话速度是特别的快。
在跨平台方面,RN版本升级特别快,它在安卓和iOS上是不一样的,你们可以试一下,基于RN在自带Package是打不出来的,安卓和iOS是有差别的,iOS比安卓多50多个模块,所以说这一块Moles Packer会帮你处理掉,每次会把不同平台的包打出来。再一个是版本的问题,版本升级特别快,每个版本都不一样,前几天是37,现在是38又有点问题了,它改动特别的频繁,如果之前是基于Packager去拆包的话随着版本去升级会非常的累,但是基于Moles Packer可以打任何的版本,这个是我们预设的目标。
说到策略,刚才提到了最简单的就是拆出来,我们将模块的模式改一下,现在很多RN里面是用CMD的模式,模块是加载性的,AMD更多用于Web端,因为浏览器不像Native这一块,它在浏览器上会影响到性能,我们之前做前端是把浏览器上好的东西放到我们Moles Packer中,把原有的CMD转成AMD,因为如果你是CMD的话是无法去加载的,除非你自己在解析代码,正常情况下还是可以把它加载下来。另外AMD还有一个好处,就是它第一次加载下来不一定执行,当你真正用的时候再去执行它的代码,这样内存就会很小。
前面是讲了它的目标和策略,后面是讲Moles Packer适用的一些场景。
现在只有一个RN项目,调用Moles Packer的话是两个,一个是Common bundl,另外一个是Common bundle自带的数据源。如果你是多个RN项目的话。
然后每个RN项目里面都有独立的Business bundle。还有第三种场景
前面其实讲的是场景和目标、策略,接下来是进入到真正的Moles Packer是如何拆的,因为主要是针对JS去做的,所以需要了解掌握一些JS方面的东西。还有一些模块管理机制,再一个就是比较火的是JS语言,已经ES6、ES7了,对开发人员来说写起来很简单,但是支持率是很差的。因为RN是基于JSX的,它里面还是运用的JSX以及生命周期,需要一个JSX解析器。接下来是CMD到AMD。只要有了这些基础的知识以后就可以给后续做一些处理,那我们讲一下公用包是如何出来的。原生打包出来是不可以用的,因为业务包打出来不知道公用包有哪些模块,所以提取模块就需要借助Source map来解析数据源,将打包后的序号与文件对应起来,之后再来定义模块。
那么业务包是如何打呢?业务包中常规的开发RN都有一个入口文件,这个业务包是以入口文件为核心,进去以后文件里可能写的是ES6、ES7的语法,你要把它进行转成ES5。另外CMD转成AMD,这一块比较复杂。再一个是做依赖分析,找模块依赖至少有3种,一种是自己写的模块,另一个Node模块,再一个是按Facebook去找,找到以后再过滤公包。
接下来讲的是技术,怎么样去把CMD转成AMD。这一部分运用到两个技术,第一个就是UglifyJS,前端经常用到的UglifyJS都是使用它的压缩功能,但是UglifyJS远不止这些功能。它可以生成AST,然后对AST进行操作。我们运用的时候用到Parser功能,将JS代码解析成AST,同时对AST进行操作,所以也会用到Transformer,操作完了以后再去生成JS代码,这时候才可以把CMD转成AMD。
再一个技术就是Babel,它的思路都是插件化的,自身也提供了很多插件,满足不了需求的话也可以自己去扩展,我们用到它主要转化React以及转化ES6。
刚才讲到了把业务包也拆出来了,它远远不止一个,是有多个,怎么进行多包执行呢,如果只拆一个公用包和业务包是很简单,那多个业务包是如何去分呢,RN只有一个注册模块,只能注册一次,如何多次把它合在一起执行呢?iOS里面可以去找到它的对应方法,在安卓上就是比较麻烦了,没有对应的方法,只能分装。那么在JS层面是如何去做呢,我们可以简单看一下这个JS代码,因为React要走生命周期都是可以这样去写。核心是在这一块,首先你要注册,一开始是通过molesRegister注册,component返回来fake,公包加载进去相当于component执行完了,这个就便于后续缓存component,下一次用Component时候将新代码拉进来执行就好了,fake执行的时候会创建一个Moleschange,就是把整个业务包作为component传进来,复制给了当前的component,RN里状态一变回立马去渲染。
接下来是讲一些使用,大家都在使用Node做开发,Moles packer 也是基于Node去做的,提供两个命令,一个是packer,另一个是packer common。
然后是打包,参数基本和RN里自带的打包是差不多的,并且会提供Node模块配合现有的常规打包工具,进行处理,我们提供了独立的打包模块。接下来是呈现出的效果。
这是三个业务线,对它们进行打包,这是不同品牌的公包,三家业务都共用一个公包,下面两个是共享一个Common,Common bridge执行完了以后,bridge是缓存起来了,点第二个 bridge2同时把第二个业务加载下来执行掉,再进入 Commonbridge2就会非常快。这就是最明显的性能方面的提升。
更多性能干货:
点击“阅读原文”申请听云产品试用