React Native資源打包和加載機制

1.基礎知識

  1. react native bundle包生成命令:
react-native bundle --entry-file index.js --bundle-output ./bundle/index.android.jsbundle --platform android --assets-dest ./bundle --dev false

    //entry-file JS文件的路徑
    //bundle-output JSbundle文件的生成目錄
    //platform 平臺 Android或iOS
    //assets-dest 資源文件的生成目錄
    //dev 開發(fā)模式
  1. 資源文件的拷貝生成:
    android:資源文件會被拷貝生成到drawable文件夾下,它的文件路徑會變成目錄名+下劃線的格式來命名原文件。如src/assets/ABCD.png會變成src_assets_ABCD.png。


    Android資源文件命名

iOS:資源文件會被拷貝生成到assets文件夾下,而資源文件則會保存原有的文件路徑。如src/assets/ABCD.png bundle之后,會生成為assets/src/assets/ABCD.png


image.png

3.react native中引用資源的方式:

比如圖片組件加載圖片或者Web組件加載HTML文件,一般是使用require命令加載對應資源的相對路徑。


image.png

2.RN的資源打包機制

rn中打包的腳本代碼在react-native/local-cli/bundle這個文件夾中,打包的邏輯代碼再buildBundle.js這個文件中。從這個方法入手,一步一步跟蹤可以發(fā)現(xiàn)。最終依賴的是metro這個Facebook專為RN打包的打包工具(https://github.com/facebook/metro)。(舊版RN代碼中是使用的內置的Packager,這里用facebook最新的RN代碼和metro代碼分析)

image.png

RN中引用的資源文件會被解析成模塊依賴,在node-haste(Facebook出的靜態(tài)資源管理包https://www.npmjs.com/package/node-haste)解析到這些資源后,會轉換成資源模塊AssetModule。具體步驟如下:

首先通過查找資源匹配來判斷是否是資源模塊,默認的資源文件后綴在defaults.js文件中。有圖片,視頻,音頻等等文件格式。


image.png

然后通過metro中一系列方法調用,最終調用到Assets文件中的getAssetData方法,生成資源模塊。

image.png

現(xiàn)在可以用我們之前打好的jsbundle文件,查看一下打好的資源模塊是什么樣的:

image.png

3.RN的資源加載機制

以圖片資源的加載為例,通過斷點可以看出獲取資源從最初的require方法為入口,后來是通過AssetRegistry.js這個類,來注冊和獲取圖片資源數(shù)據(jù)。通過一個數(shù)組來維護項目中所使用的本地圖片資源。

image.png
//封裝的圖片資源數(shù)據(jù)
export type PackagerAsset = {
  +__packager_asset: boolean,
  +fileSystemLocation: string,
  +httpServerLocation: string,
  +width: ?number,
  +height: ?number,
  +scales: Array<number>,
  +hash: string,
  +name: string,
  +type: string,
};

const assets: Array<PackagerAsset> = [];

//注冊圖片資源
function registerAsset(asset: PackagerAsset): number {
  // `push` returns new array length, so the first asset will
  // get id 1 (not 0) to make the value truthy
  return assets.push(asset);
}

//通過ID獲取圖片資源
function getAssetByID(assetId: number): PackagerAsset {
  return assets[assetId - 1];
}

圖片資源數(shù)據(jù)中是資源的相對路徑,在圖片組件中并不是能直接使用的Uri。在這之間有一次資源的解析。查找圖片組件的源碼Image.ios.js或Image.android.js,發(fā)現(xiàn)都通過resolveAssetSouce方法處理了。最終在defaultAsset方法中能清楚的看到圖片加載可以分為來自網(wǎng)絡,資源或是本地文件系統(tǒng)中。

image.png
function resolveAssetSource(source: any): ?ResolvedAssetSource {
  if (typeof source === 'object') {//加載{uri:XXXX}這種網(wǎng)絡資源
    return source;
  }
  const asset = AssetRegistry.getAssetByID(source);//獲取資源ID
  if (!asset) {
    return null;
  }
  const resolver = new AssetSourceResolver(
    getDevServerURL(),
    getScriptURL(),
    asset,
  );
  if (_customSourceTransformer) {//自定義資源解析
    return _customSourceTransformer(resolver);
  }
  return resolver.defaultAsset();//默認資源解析方法
}

//AssetSourceResolver.js
class AssetSourceResolver {
  serverUrl: ?string;
  // where the jsbundle is being run from
  jsbundleUrl: ?string;
  // the asset to resolve
  asset: PackagerAsset;

  constructor(serverUrl: ?string, jsbundleUrl: ?string, asset: PackagerAsset) {
    this.serverUrl = serverUrl;
    this.jsbundleUrl = jsbundleUrl;
    this.asset = asset;
  }

  isLoadedFromServer(): boolean {
    return !!this.serverUrl;
  }

  isLoadedFromFileSystem(): boolean {
    return !!(this.jsbundleUrl && this.jsbundleUrl.startsWith('file://'));
  }

  defaultAsset(): ResolvedAssetSource {
    if (this.isLoadedFromServer()) {//加載來自網(wǎng)絡的資源,jsbundle資源來自網(wǎng)絡,例如debug模式
      return this.assetServerURL();
    }

    if (Platform.OS === 'android') {
      return this.isLoadedFromFileSystem()//加載來自文件系統(tǒng)的資源
        ? this.drawableFolderInBundle()
        : this.resourceIdentifierWithoutScale();//加載來自Asset資源中的文件
    } else {
      return this.scaledAssetURLNearBundle();
    }
  }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容