React Native 圖片資源那些事

react-native中使用Image組件來顯示圖片,表面上和htmlimg標籤大同小異,可是其source屬性包含的邏輯缺複雜的多,同時也和bundle運行的方式也有關係。html

本篇文章將重點講解下Image中圖片解析邏輯,以及如何自定義圖片解析邏輯。react

1. 打包結構

react-native bundle --entry-file index.js --bundle-output ./bundle/ios/main.jsbundle --platform ios --assets-dest ./bundle/ios --dev false 

react-native bundle --entry-file index.js --bundle-output ./bundle/android/index.bundle --platform android --assets-dest ./bundle/android --dev false

首先看下iOSandroid打包結果:android

clipboard.png

iOS會按照項目結構輸出圖片資源到咱們制定的目錄assets下。ios

android中,drawable-mdpidrawable-xhdpidrawable-xxhdpi等存放不一樣分辨率屏幕下的圖片,文件名的組成是目錄和圖片名稱經過_拼接。react-native

2. 圖片連接生成邏輯

代碼位於react-native/Libraries/Image/resolveAssetSource服務器

resolveAssetSource.js最終會export如下內容:app

module.exports = resolveAssetSource;
module.exports.pickScale = AssetSourceResolver.pickScale;
module.exports.setCustomSourceTransformer = setCustomSourceTransformer;
  • resolveAssetSource: 圖片地址拼接工具
  • pickScale: 像素比工具
  • setCustomSourceTransformer: 自定義圖片連接處理方式

這裏的重點是resolveAssetSource,它會處理Imagesource,並返回圖片地址。函數

建立了AssetSourceResolver,並傳入getDevServerURL()getScriptURL()asset工具

若是存在自定義處理函數_customSourceTransformer,就返回它的執行結果。它的設置就是經過setCustomSourceTransformer來完成的。ui

不然就調用resolver.defaultAsset,使用默認的邏輯處理圖片。

/**
 * `source` is either a number (opaque type returned by require('./foo.png'))
 * or an `ImageSource` like { uri: '<http location || file path>' }
 */
function resolveAssetSource(source: any): ?ResolvedAssetSource {
  if (typeof source === 'object') {
    return source;
  }

  const asset = AssetRegistry.getAssetByID(source);
  if (!asset) {
    return null;
  }

  const resolver = new AssetSourceResolver(
    getDevServerURL(),
    getScriptURL(),
    asset,
  );
  if (_customSourceTransformer) {
    return _customSourceTransformer(resolver);
  }
  return resolver.defaultAsset();
}

接下來看AssetSourceResolver.js的代碼。

咱們前文初始化AssetSourceResolver,設置了三個參數:

  • serverUrl: 服務地址,格式爲"http://www.xxx.com"
  • jsbundleUrl: bundle所在位置
  • asset

裏面包含了最終返回圖片的邏輯:defaultAsset,咱們分析以後能夠獲得:

bundle放在server

經過以下代碼拼接圖片地址,這裏使用serverUrl要求bundle文件和圖片在同級目錄而且在域名下,中間不能有二級目錄。

this.fromSource(
      this.serverUrl +
        getScaledAssetPath(this.asset) +
        '?platform=' +
        Platform.OS +
        '&hash=' +
        this.asset.hash,
    );
解決方案是經過 setCustomSourceTransformer替換 serverUrl,改成 jsbundleUrl

bundle內置在app

這裏不一樣平臺的處理方式又不同。

iOS從資源中加載圖片

android分爲兩種:資源和文件系統(file://)

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;
  }
  
  ...
  
  defaultAsset(): ResolvedAssetSource {
    if (this.isLoadedFromServer()) {
      return this.assetServerURL();
    }

    if (Platform.OS === 'android') {
      return this.isLoadedFromFileSystem()
        ? this.drawableFolderInBundle()
        : this.resourceIdentifierWithoutScale();
    } else {
      return this.scaledAssetURLNearBundle();
    }
  }

流程圖

3. 寫在結尾

咱們瞭解Image組件的圖片邏輯以後,就能夠按需調整了,經過調用setCustomSourceTransformer傳入自定義函數來控制最終圖片的訪問地址。

我在項目中的處理是bundle部署在服務器上,這種方式會有兩個問題:

  1. 圖片資源是從域名開始查找,放置在多級目錄後就沒法訪問到圖片
  2. 安卓跳過了drawable-x目錄

上面的問題都是圖片沒法顯示,不知道看到文章的你是否也想到了解決辦法?

本文同步發表於做者博客: React Native 圖片資源那些事
相關文章
相關標籤/搜索