React Native 資源包拆分實(shí)踐(一)

資源包拆分背景

React Native 應(yīng)用默認(rèn)會(huì)將我們的 JS 代碼打包成一個(gè)文件,當(dāng)我們的 React Native 應(yīng)用變得很龐大了以后,一次性下載所有 JS 代碼往往耗時(shí)很長(zhǎng)。這時(shí)我們可能會(huì)想到可以通過(guò)按需加載來(lái)進(jìn)行優(yōu)化,而按需加載的首要任務(wù)就是對(duì)代碼進(jìn)行拆分。

通過(guò)分析ReactNative頁(yè)面的JSBundle文件, 可發(fā)現(xiàn)一個(gè)完整的ReactNative頁(yè)面代碼結(jié)構(gòu)可以分為模塊引用、模塊定義、模塊注冊(cè)三部分。

  • 模塊引用:主要是全局模塊的定義,
  • 模塊定義:主要是組件的定義(原生組件、自定義組件),
  • 模塊注冊(cè):主要是初始化以及入口函數(shù)的執(zhí)行。

拆分方案

metro是 React Native 的官方打包工具,類似于 web 開發(fā)常用的打包工具 Webpack。在React Native 0.56版本 之后,metro開放了 --config <path/to/config> 參數(shù),可用于配置自定義的打包過(guò)程。本文的metro拆包方案是基于React Native 0.56以后的版本。

借助metro可以進(jìn)行JS Bundle 拆包:公共基礎(chǔ)包和業(yè)務(wù)包。多個(gè)業(yè)務(wù)共用一個(gè)公共基礎(chǔ)包,公共基礎(chǔ)包可以內(nèi)置在APP中并進(jìn)行預(yù)加載,用戶進(jìn)入某個(gè)業(yè)務(wù)時(shí),再進(jìn)行業(yè)務(wù)包的加載。


JS Bundle 拆包

公共基礎(chǔ)包

先建立一個(gè) common.js 文件,在里面引入了所有的基礎(chǔ)模塊。然后, metro 以這個(gè) common.js 為入口文件,打一個(gè) common.bundle 文件,同時(shí)要記錄所有的基礎(chǔ)模塊的 moduleId。

// common.js 示例

require('react');
require('react-native');
// ... 可以繼續(xù)引入更多的公共基礎(chǔ)模塊
為了避免 moduleId 重復(fù),目前業(yè)內(nèi)主流的做法是把模塊的文件路徑當(dāng)作 moduleId。metro 暴露了 createModuleIdFactory() 這個(gè)函數(shù),可以在這個(gè)函數(shù)里自定義moduleId 的生成邏輯。

// metro.common.config.js

const fs = require('fs');

function getModuleId(path) {
  // 根據(jù)文件的相對(duì)路徑構(gòu)建 moduleId
  const projectRootPath = __dirname;
  let moduleId = path.substr(projectRootPath.length + 1);
  return moduleId;
}

function createModuleIdFactory() {
    return getModuleId
}

function processModuleFilter(module) {
    const moduleId = getModuleId(module['path'])
    // 把 moduleId 寫入 moduleIdList.txt 文件,記錄基礎(chǔ)模塊的moduleId
    fs.appendFileSync('./moduleIdList.txt', `${moduleId}\n`);
    return true
}

module.exports = {
  serializer: {
    createModuleIdFactory,
    processModuleFilter,
  },
};

然后運(yùn)行命令行命令打包即可:

# 打包平臺(tái):android 和 ios
# 打包配置文件:metro.common.config.js
# 打包入口文件:common.js
# 輸出路徑:bundle/common.android.bundle 和 bundle/common.ios.bundle

npx react-native bundle --platform android --config metro.common.config.js --dev false --entry-file common.js --bundle-output bundle/common.android.bundle

npx react-native bundle --platform ios --config metro.common.config.js --dev false --entry-file common.js --bundle-output bundle/common.ios.bundle
業(yè)務(wù)包構(gòu)建

對(duì)業(yè)務(wù)進(jìn)行打包,metro 的打包入口文件就是業(yè)務(wù)項(xiàng)目入口文件index.js。

// index.js
class App extends Component {
    render() {
        return (
            <View>
                <Text>
                    hello business
                </Text>
            </View>
        )
    }
}

AppRegistry.registerComponent("business", () => App);

注意要在打包過(guò)程中要過(guò)濾掉上一步記錄的基礎(chǔ)模塊的moduleId,這樣打包結(jié)果就只有業(yè)務(wù)代碼了。

metro 提供了 processModuleFilter() 這個(gè)方法,借助它可以實(shí)現(xiàn)模塊的過(guò)濾:

// metro.business.config.js

const fs = require('fs');

// 讀取 moduleIdList.txt,轉(zhuǎn)換為數(shù)組
const moduleIdList = fs.readFileSync('./moduleIdList.txt', 'utf8').toString().split('\n');

function getModuleId(path) {
  // 根據(jù)文件的相對(duì)路徑構(gòu)建 moduleId
  const projectRootPath = __dirname;
  let moduleId = path.substr(projectRootPath.length + 1);
  return moduleId;
}

function createModuleIdFactory() {
    return getModuleId
}

function processModuleFilter(module) {
    //const modulePath = getProjectPath(module['path'])
    const moduleId = getModuleId(module['path'])
    if (moduleIdList.indexOf(mouduleId) >= 0) {
      // 過(guò)濾掉上一步記錄的基礎(chǔ)模塊的moduleId
      return false;
    }
    return true;
}

module.exports = {
  serializer: {
    createModuleIdFactory,
    processModuleFilter,
};

最后運(yùn)行命令行命令打包即可:

# 打包平臺(tái):android 和 ios
# 打包配置文件:metro.business.config.js
# 打包入口文件:index.js
# 輸出路徑:bundle/business.android.bundle 和 bundle/business.ios.bundle

npx react-native bundle --platform android --config metro.business.config.js --dev false --entry-file index.js --bundle-output bundle/business.android.bundle

npx react-native bundle --platform ios --config metro.business.config.js --dev false --entry-file index.js --bundle-output bundle/business.ios.bundle
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容