React Native 分包哪家强?看这文就够了!

百家 作者:程序人生 2019-01-16 07:44:37


作者 | 魔笛

责编 | 伍杏玲


前景

自2015年Facebook发布React Native以来,目前已经是跨平台开发的主流产品,但是随着业务复杂度越来越高,单一的JSBundle越来越大,已经不能满足用户需求,需要拆分业务包&基础包。

在拆包之前先了解一下React Native 打包流程,以及JSBundle的结构


React Native 打包过程分析

Metro Bundler

RN 官方从 0.46.0 版本开始,出于对打包速度、可靠性和更好的可拓展性等方面的考虑,将 RN 的打包模块 packager 从 RN 项目中分离,重新命名为 Metro Bundler。 详细参考

此图参考react-native打包

执行JSBundle命令

react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./index.ios.bundle --sourcemap-output ./index.ios.map --assets-dest ./asserts

  • entry-file:即入口文件,打包时以该文件作为入口,一步步进行模块分析处理。

  • platform:用于区分打包什么平台的 bundle (ios or android, 默认ios)

  • dev:用于区分 bundle 使用环境,非 dev 时,会对代码进行 minified

  • bundle-output:打包产物输出地址,即打包好的 bundle 存放地址

  • sourcemap-output:打包时生成对应的 sourcemap 文件存放地址,在跟踪查找错误或崩溃时,能帮助开发快速定位到代码

  • assets-dest:bundle 中使用的静态资源文件存放地址

  • --verbose: 打印详情

  • --reset-cache: 清除缓存文件

  • --config: CLI文件路径

Polyfill

Metro Bundler 在进入正式拆包前,先在 js 解释器上挂载 global.DEV 变量,该变量主要用于区分打包执行环境,同时定义了模块系统函数,包括 __d(即 define)函数、require 函数,这样 RN 也就有了自身的模块系统。另外,RN 对部分 es6、es7 的方法 Polyfill 支持,包括 Array、Object 和 Number 等。


React Native JSBundle 结构


以最基础的RN项目的 Bundle 为例,可以看到 Bundle 文件中大致定义了四个模块:

  1. var 声明的变量,对当前运行环境的定义,Bundle 的启动时间、Process进程环境相关信息

  2. (function() { })() 闭包中定义的代码块,其中定义了对 define(__d)、  require(__r)、clear(__c) 的支持,以及 module(React Native及第三方dependences依赖的module) 的加载逻辑

  3. __d 定义的代码块,包括RN框架源码 js 部分、自定义js代码部分、图片资源信息,供 require 引入使用

  4. __r 定义的代码块,找到 __d 定义的代码块 并执行

总结以上,Bundle 文件大致包含三部分:

Polyfills:最先执行的一些 function,ES6特性支持,定义模块声明方法等

modules:模块声明,以 __d开头,定义各式各样的module,其中包括了React Native 的module(StatusBar、View、Text ...),引入的第三方 module 等

require:执行 InitializeCore 和 Entry File,最后一行执行 require(0)

__d 的执行对应于 nodule_modules / metro / lib / polyfills / require.js 文件中的 define 方法

require 的执行对应于 nodule_modules / metro / lib / polyfills / require.js 文件中的 metroRequire 方法


React Native 拆包


了解了JSBundle的打包过程以及JSBundle的结构,下面我们讨论一下拆包。

随着业务不断增加,模块化自然是首要考虑的问题。

然后业务1,2,3分别用RN容器加载,但是这样的结构有显而易见两个问题:

  • 每个业务界面打开是会有明显的白屏

  • 每个业务中jsbundle会有很多重复模块

根据上面JSBundle的结构, 可以将JSBundle拆分成以下模块:

  • base module,是可复用基础包,里面包括React Native模块,view, text, button和一些可复用的三方模块。

  • 业务1,2,3 module,是纯js业务代码,可动态加载。


多JSBundle打包


目前推荐官方提供Metro Bundle打包服务基础上做拆包,如果团队人员充沛,可考虑自研打包服务,Metro Bundle 配置参数(https://facebook.github.io/metro/docs/en/configuration)

主要是序列化的时候:

  • 配置createModuleIdFactory函数生成自定义的module id, 生成module id可以用文件绝对路径,也可以做一次hash或者md5,保证唯一。

    因为 metro 打包时,会以递增的方式给每个模块分配一个ID,在文件调用时直接调用对应的ID号。在拆分 bundle 后,如果我们的基础包有依赖模块的变动,整个模块调用的3, ID都会错位。所以要采用更加稳定健壮的ID生成方式。

  • 配置processModuleFilter 过滤包,函数传入的是module信息,返回是boolean值,如果是false就过滤不打包, 例如业务不需要打node_module中的包。

  • 静态资源处理。因为打包生成的静态资源根目录是固定的assets,为了方便灵活组织资源内容,我们添加对自定义静态资源根目录的功能支持。

多 bundle 静态资源最终会合并到一个目录下去,这是最节约资源的一种结构,防止重复资源出现

|- assets/
    |
- base/
    |- node_modules/
    |
- bundle1_resource/
    |- bundle2_resource/

具体可参考, react-native-multibundler


多业务打包部署


每个业务包,都包含自己的基础依赖base.js:

  1. 业务(1,2,3)发布一个版本,push tag触发CI server;

  2. CI 触发 WBRNpackage服务;

  3. WBRNpackage服务根据base.js合成公用的base.js;

  4. 分别打出对应业务的JSBundle, 基础base.jsbundle, 并合成静态资源文件


总结


截止目前我们解决了几个问题:

  • 模块业务分离,每个业务是一个单独的包

  • 由于之前加载了基础包,原生容器打开对应的业务包,可以达到秒开的效果

  • 在多容器的情况下,共用一个基础包,防止bundle资源重复

  • 利用CI自动化自动部署JSBunlde和静态资源

作者简介:魔笛,从事移动端开发7年多的经验,目前在某互联网金融公司作为iOS端leader工作。 


 热 文 推 荐 

罗永浩“咬定”微信不放松

找工作时单位普遍要求35岁以下,那35岁以上的人都干嘛去了?

华为波兰销售总监被捕!

吃亏的程序员,是如何拿到了9个月的年终奖?

2亿简历遭泄漏,到底谁的锅?

8年后重登王座,Python再度成为TIOBE年度编程语言

Grin带火的MinbleWimble技术,到底是个什么鬼?

刚刚!程序员集体荣获2个冠军,这份2018IT报告还说这些!

print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!");
cout < < "点个好看吧!" < < endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"

点击“阅读原文”,打开 CSDN App 阅读更贴心!

喜欢就点击“好看”吧

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接