什么是JSBundle
JSBundle 是 JS代碼打包后的產(chǎn)物枪蘑,在React-native里面主要是通過(guò)react-native-cli提供的命令進(jìn)行打包磺樱。跟網(wǎng)頁(yè)類似秫逝,一個(gè)RN項(xiàng)目除了代碼還會(huì)有資源文件古程,比如本地圖片煎殷、JSON等屯伞,這些都會(huì)放到跟JSbundle同級(jí)的assets目錄下。這些文件可以內(nèi)置在原生的工程里豪直,在原生工程啟動(dòng)時(shí)通過(guò)RN官方提供的方法加載JSbundle劣摇,并放到JS引擎中執(zhí)行。也可以打成壓縮包之后通過(guò)網(wǎng)絡(luò)下載(也就是熱更新)后再在本地執(zhí)行弓乙。
react-native-cli
React-native官方提供的命令行工具末融,里面包含了拉取react-native模版工程(init命令)钧惧、診斷運(yùn)行環(huán)境(doctor命令)、打包(bundle命令)勾习,以及遠(yuǎn)程調(diào)試(本地node服務(wù))所用到的代碼垢乙。默認(rèn)通過(guò)npm依賴的方式集成到react-native源碼中,也可以單獨(dú)下載(https://github.com/react-native-community/cli#documentation)语卤,具體命令可通過(guò)官方文檔或npx react-native --help 查閱
JSBundle格式
原始格式
原始格式是純文本的JS代碼追逮,默認(rèn)情況下會(huì)進(jìn)行壓縮和混淆
當(dāng)打包時(shí)關(guān)閉掉-minify選項(xiàng)后,可以看到原始的代碼
JSbundle里每一行都是一個(gè)module粹舵,也就是一個(gè)文件的內(nèi)容钮孵,在每一行的末尾會(huì)有該module的ID和所依賴的其他module的ID
moduleId是根據(jù)編譯時(shí)的順序生成的,默認(rèn)是從0開(kāi)始生成眼滤,按文件依次遞增巴席。前面的內(nèi)容都是框架自帶的module,我們自己寫的module通常在后面诅需。
本文是根據(jù)官方提供的awesome project模版拉取的代碼并生成的jsbunble漾唉,可以跟源碼對(duì)比看看有什么差異。
官方為了縮減JSbundle的大小堰塌,對(duì)很多函數(shù)做了簡(jiǎn)化處理赵刑,例如declare變成了__d, require變成了__r。
Hermes二進(jìn)制格式
二進(jìn)制格式的本質(zhì)是字節(jié)碼场刑,字節(jié)碼是JS轉(zhuǎn)成可執(zhí)行代碼的中間形式般此。由于JS代碼在 JS引擎里面需要編譯為字節(jié)碼或者機(jī)器碼才能執(zhí)行,這一階段比較耗時(shí)牵现,而且每次啟動(dòng)都是執(zhí)行铐懊,明顯是屬于重復(fù)工作。為了減少這個(gè)時(shí)間瞎疼,官方推出了hermes二進(jìn)制格式科乎,也就是我們說(shuō)的字節(jié)碼,支持預(yù)編譯并且可以緩存在本地贼急,減少二次編譯茅茂,甚至可以在生成JSbundle的時(shí)候就編譯為二進(jìn)制格式。關(guān)于字節(jié)碼的詳細(xì)解釋竿裂,可以參考我這篇博客(http://www.reibang.com/p/af772cc66428),這里就不詳細(xì)解釋了玉吁。
RAM格式
RAM也是一種二進(jìn)制格式,推出的目的是為了壓縮包大小腻异,主要是將jsbundle按模塊拆分為單個(gè)的文件以支持按需加載进副,但是由于只支持iOS,并沒(méi)有真正推廣起來(lái)。想了解可以看官方介紹(https://facebook.github.io/metro/docs/bundling)影斑,不推薦深入研究给赞。
打包腳本
我們使用react-native bundle命令來(lái)打包,假設(shè)打出來(lái)的包都放在 build 這個(gè)目錄下矫户,我們需要執(zhí)行以下指令:
這生成index.android.bundle和index.android.bundle.packager.map片迅,分別是JSbundle和sourceMap文件
npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./build/index.android.bundle --sourcemap-output ./build/index.android.bundle.packager.map
生成hermes二進(jìn)制文件index.android.bundle.hbc及其與源碼的映射文件index.android.bundle.hbc.map(主要是記錄模塊的VLQ編碼與二進(jìn)制文件中對(duì)應(yīng)函數(shù)的偏移量的映射關(guān)系)。注意這里-output-source-map的值是上一步生成的JSBundle皆辽,并非我們通常所說(shuō)的sourcemap文件柑蛇。不同的版本hermesc的位置略有不同,可執(zhí)行
./node_modules/hermes-engine/osx-bin/hermesc -emit-binary -out ./build/index.android.bundle.hbc -output-source-map ./build/index.android.bundle
或
./node_modules/react-native/sdks/hermesc/osx-bin/hermesc -emit-binary -out ./build/index.android.bundle.hbc -output-source-map ./build/index.android.bundle
根據(jù)第1步生成的sourcemap以及第2步生成的映射文件生成二進(jìn)制文件的sourcemap驱闷。這一步不是必須的耻台,但是如果你想通過(guò)sentry之類的錯(cuò)誤收集平臺(tái)來(lái)找到出錯(cuò)的代碼,并且JSbundle是使用了hermes二進(jìn)制格式的空另,就一定要上傳這個(gè)sourceMap盆耽。
./node_modules/react-native/scripts/compose-source-maps.js ./build/index.android.bundle.packager.map ./build/index.android.bundle.hbc.map -o ./build/index.android.bundle.map
metro
metro是react-native專用的打包工具,有點(diǎn)類似web開(kāi)發(fā)里面的webpack扼菠。前面說(shuō)的react-native bundle命令背后就是用的metro(可參考代碼 https://github.com/react-native-community/cli/tree/main/packages/cli-plugin-metro)摄杂,關(guān)于metro的使用可以參考官方文檔(https://facebook.github.io/metro/docs/concepts)
metro 大致可以分為resolver、transformer和Serialization三個(gè)階段循榆,分別是解析源碼生成module的依賴圖析恢、轉(zhuǎn)換module的格式已被目標(biāo)平臺(tái)所理解以及序列化生成最終產(chǎn)物,三個(gè)階段官方有提供默認(rèn)的實(shí)現(xiàn)(比如transformer是使用了babel)冯痢,也提供了配置來(lái)替換一些關(guān)鍵函數(shù)氮昧。metro內(nèi)部會(huì)根據(jù)依賴圖的變化計(jì)算新增、修改和刪除的模塊浦楣,并且通過(guò)緩存transformer的結(jié)果來(lái)提升debug時(shí)的熱更新效率。關(guān)于metro有很多博客介紹咪辱,可以參考http://www.reibang.com/p/5730da61132f振劳。
我們做拆包,主要是針對(duì)Serialization這個(gè)階段做修改油狂,修改的函數(shù)包括createModuleIdFactory(自定義模塊ID的生成規(guī)則历恐,確保唯一即可)、processModuleFilter(根據(jù)module信息判斷是否已經(jīng)處理過(guò),打業(yè)務(wù)包需要)
hermesc
這是hermes的一個(gè)命令行工具专筷,封裝了hemres用到的常用函數(shù)弱贼,其中-emit-binary 功能是根據(jù)傳入的路徑找到j(luò)sbundle,加載內(nèi)容磷蛹,然后一行一行的解析吮旅,將JS編譯為字節(jié)碼,同時(shí)生成映射文件味咳。這個(gè)工具也支持dump字節(jié)碼庇勃、AST檬嘀、IR以及解析JSX、ts等功能责嚷,可以說(shuō)是非常全面了鸳兽。可以輸入./node_modules/react-native/sdks/hermesc/osx-bin/hermesc --help
查看所有的命令