携程CRN源码详解之拆包(四)——业务打包

1、业务包

    除了rn_common的包含业务逻辑的代码会被CRN打成业务包。CRN希望把业务包做得尽量地可以按需加载,因此CRN使用了metro本来就有的rambundle功能。rambundle在之前叫做unbundle,这种打包方式能把bundle按模块分散成多个独立的js文件,加载页面的时候只会加载需要的js文件,大大提升了页面的加载速度。

2、打包命令

 cmd = 'node node_modules/react-native/cli.js ram-bundle --config rn-cli.config.js ' + buildCommand + ' --assets-dest bundle_output/publish';
        logOutPut.log(cmd);
        execSync(cmd, { stdio: 'inherit' });

这里明确指明使用ram-bundle方式打包,跟rn_common打包使用的bundle有明显区别。

3、过滤掉rn_common

iOS的ram-bundle是一个整体的问题,为了和Android统一,crn修改了RamBundle.js

//RamBundle.js
function save(bundle, options, log) {

  //CRN BEGIN
  //处理ios打包成多文件目标
  return asAssets(bundle, options, log); //CRN END
  //ORIGINAL:
  //return options.platform === 'android' && !options.indexedRamBundle
  //   ? asAssets(bundle, options, log)
  //   : asIndexedFile(bundle, options, log);
  //ORIGINAL-END
}

如果过滤,重写as-assets.js的写文件方法

//as-assets.js

function writeModuleFile(module, modulesDir, encoding) {
  const code = module.code,
    id = module.id; 
  //CRN BEGIN 输出剔除common包中的模块
  if (id < 666666) {
    return Promise.resolve();
  } //CRN END

  return writeFile(path.join(modulesDir, id + ".js"), code, encoding);
}

只要id小于666666就过滤掉,这过滤掉的就是rn_common的内容,因为rn_common模块id是从0开始的,而你依赖的模块再怎么上天也上不了666666这个数字。而业务id是从666666开始的,因此这里过滤后的js文件都是业务模块的文件。

4、资源文件加载

   以上的步骤已经完成了包括了资源的打包,因为资源的打包逻辑本来就不需要修改,换句话说:打业务包的时候不需要过滤rn_common的资源,因为crn框架中rn_common只有代码没有资源。而资源文件的加载需要修改。

  crn为了让Android和iOS使用统一的资源文件,强行把本来不一样的资源打包方式改成一样,即改成iOS的资源格式

//AssetSourceResolver.js
 defaultAsset(): ResolvedAssetSource {
    if (this.isLoadedFromServer()) {
      return this.assetServerURL();
    }
    //CRN BEGIN
    //资源加载IOS和Android保持一致
    // if (Platform.OS === 'android') {
    //   return this.isLoadedFromFileSystem()
    //     ? this.drawableFolderInBundle()
    //     : this.resourceIdentifierWithoutScale();
    // } else {
      return this.scaledAssetURLNearBundle();
    // }
    //CRN END
  }

可以看到这里的改动直接把Android的逻辑去掉了,简单有效

这里crn为了能让支持让业务包放在多个目录下加载也改了资源的加载方式,按照rn原来的逻辑所有的代码和资源必须放在同一个目录。

//resolveAssetSource.js
function getSourceCodeScriptURL(): ?string {
  //CRN BEGIN
  //拆包后的路径解析相关
  if (global.CRN_PACKAGE_PATH) {
    return global.CRN_PACKAGE_PATH;
  }
  //CRN END
  if (_sourceCodeScriptURL) {
    return _sourceCodeScriptURL;
  }
... ... ...
}

crn加载业务包的时候会将业务包路径存放到全局变量CRN_PACKAGE_PATH中,详见crn_common_entry。这里返回CRN_PACKAGE_PATH代表后面资源文件都在这个目录下查找,这就完成能在对应业务bundle目录下查询到对应的资源文件。

5、统一业务包打包产物

    大家都知道由于Android和iOS平台的差异性,有一些js模块它是有分Android和iOS版本的,这也导致Android和iOS打出来的js包会有所不同,这里CRN对此做了处理。

//build.js
**
 * 生成js-diffs文件夹,存储IOS和Android打包差异化代码
 */
function mergeBundle() {
    var command = "diff -q " + path.resolve(currentPath, 'bundle_output/publish/js-modules/') + " " + path.resolve(currentPath, 'bundle_output_other/publish/js-modules/');
    logOutPut.log(command);
    try {
        execSync(command);
    } catch (error) {
        var diff = error.stdout.toString()
        logOutPut.log(diff);
        var regexp = /6+[0-9]*.js/g;
        var arr = diff.match(regexp)
        arr = [...new Set(arr)];
        logOutPut.log(arr);
        var dest = path.resolve(currentPath, 'bundle_output_other/publish/js-diffs/');
        fs.ensureDirSync(dest)
        arr.forEach(function (item, i) {
            var src = path.resolve(currentPath, 'bundle_output/publish/js-modules/' + item);
            fs.copySync(src, path.resolve(dest, item));
        });
    } finally {
        deleteFile(path.resolve(currentPath, './bundle_output/'));
        fs.copySync('./bundle_output_other', './bundle_output');
        logOutPut.log('mergeBundle finish');
    }
}

这算出Android和iOS的差异的地方并把差异的js文件保存到js-diffs文件夹中,Android手机加载业务包的时候优先查找js-diffs文件下的js模块。

6、后续

    到这里crn的打包的90%的逻辑已经明了,我们需要注意的是所有的业务包都必须从666666 id开始算起,业务包就是剔除了的rn_common的打包,这个是没有react-native-multibundler灵活的。不过CRN的生态已经完善,这种不灵活的做法也够应对了。

  crn的拆包已经基本讲完,总体来说CRN的优点很明显,提供了基本的拆包思路,使用patch方式修改源码高效方便,为了方便使用做了很多细节上的优化。不过还有优化的空间的,rn_common不够灵活,业务包之间的交互没有提供方案,可能这是为了多部门协作而做的妥协吧。

  • 打包支持框架和业务代码拆分
  • 支持框架代码后台预加载
  • 打包支持增量编译(同一模块,两次打包模块ID不变)
  • iOS&Android统一一套打包产物
  • 首屏加载性能统计
  • LazyRequire

剩下的这两个功能算是对拆包的锦上添花,具体的实现我就不解析了,可以看看官方的介绍:

https://mp.weixin.qq.com/s/Z1GUJW3qBqDGH1jnGt5qAg

 

你可能感兴趣的:(react,naitve)