天天看點

RN Metro打包流程

1. 指令參數解析:

react-native bundle --dev false  --platform android  --entry-file index.js --config bundle.main.js --bundle-output ./CodePush/index.android.bundle --assets-dest ./CodePush --sourcemap-output ./CodePush/index.android.bundle.map "

bundle檔案包含四層:

1.var聲明層,主要包含目前環境資訊、bundle啟動時間和目前程序相關資訊

2.Polyfill層,!(function(r)來頭部分代表polyfill層,定義了對 __define()、__require()和__clear()的支援,還有包含react-native和第三方的module

3.子產品定義層,包含 __define代碼塊、RN架構源碼js部分和自定義js部分以及圖檔資源資訊,供require引用

4.require層,引用require來執行define子產品的代碼

--dev : false: product包   true: dev包

--platform: 對應的平台

--entry-file:入口檔案

--config: 額外配置

 --bundle-output:生成的bundle檔案輸出位置

--assets-dest: 圖檔等資源輸出的位置

--sourcemap-output: 映射檔案輸出的位置

2. metro 打包服務啟動:

在node_modules/react-native/node_modules/@react-native-community/cli/build/commands/bundle/buildBundle.js檔案中預設導出的 buildBundle 方法才是整個react-native bundle執行的入口。

在入口中主要做了如下幾件事情:

-合并 metro 預設配置和自定義配置,并設定 maxWorkers,resetCache

const config = await (0, _loadMetroConfig.default)(ctx, {      
    maxWorkers: args.maxWorkers,      
    resetCache: args.resetCache,      
    config: args.config,      
});      

-根據解析得到參數,建構 requestOptions,傳遞給打包函數

const requestOpts = {      
    entryFile: args.entryFile,      
    sourceMapUrl,      
    dev: args.dev,      
    minify: args.minify !== undefined ? args.minify : !args.dev,      
    platform: args.platform,      
};      

-執行個體化 metro Server

const server = new (_Server()).default(config);      

-啟動 metro 建構 bundle

-處理資源檔案,解析

-關閉 Metro Server

try {      
    // 啟動打包      
    const bundle = await output.build(server, requestOpts);      
    // 将打包生成的bundle儲存到對應的目錄      
    await output.save(bundle, args, _cliTools().logger.info);       
    // 處理資源檔案,解析,并在下一步儲存在--assets-dest指定的位置      
    const outputAssets = await server.getAssets({      
      ..._Server().default.DEFAULT_BUNDLE_OPTIONS,      
      ...requestOpts,      
      bundleType: "todo",      
    });      
    // 儲存資源檔案到指定目錄      
    return await (0, _saveAssets.default)(      
      outputAssets,      
      args.platform,      
      args.assetsDest      
    );      
} finally {      
    // 停止metro 打包服務      
    server.end();      
}      
為什麼用output.build啟動打包?      

Output對象是“metro/src/shared/output/bundle”檔案中的buildBundle函數:

function buildBundle(packagerClient, requestOptions) {      
  return packagerClient.build(      
    _objectSpread({}, Server.DEFAULT_BUNDLE_OPTIONS, requestOptions, {      
      bundleType: "bundle",      
    })      
)      
packagerClient:是output.build(server, requestOpts)傳過來的serve對象      

packagerClient.build的build函數: 是server的build函數:

1.将參過來的參數,按子產品拆分,用splitBundleOptions(options)函數拆分成四個部分:entryFile、transformOptions、serializerOptions、onProgress

2.解析和轉換,用buildGraph(entryFile,transformOptions, {onProgress})函數,最終生成prepend和graph對象

3.建構入口檔案路徑

const entryPoint = path.resolve(projectRoot, entryFile)

4.初始化建構參數,此處的參數來源于: 指令行和自定義metro.config.js配置

const bundle = baseJSBundle(entryPoint, prepend, graph, {

      processModuleFilter: xxxxx,      
createModuleId: xxxx, // createModuleIdFactory給每個module生成id;       
getRunModuleStatement: xxxx,  //給方法簽名      
dev: transformOptions.dev,      
projectRoot: _this2._config.projectRoot,      
modulesOnly: serializerOptions.modulesOnly,      
runBeforeMainModule: _this2._config.serializer.getModulesRunBeforeMainModule      
(      
    path.relative(_this2._config.projectRoot, entryPoint)      
), // 指定在主子產品前運作的子產品      
runModule: serializerOptions.runModule,      
sourceMapUrl: serializerOptions.sourceMapUrl,      
sourceUrl: serializerOptions.sourceUrl,      
inlineSourceMap: xxxx,      

})

5.将js module進行排序和字元串拼接,生成最終代碼      
bundleToString(bundle).code      

更詳細:https://segmentfault.com/a/1190000038346994

繼續閱讀