React-Native-图片资源那些事

41次阅读

共计 2850 个字符,预计需要花费 8 分钟才能阅读完成。

react-native中使用 Image 组件来显示图片,表面上和 htmlimg标签大同小异,但是其 source 属性包含的逻辑缺复杂的多,同时也和 bundle 运行的方式也有关系。

本篇文章将重点讲解下 Image 中图片解析逻辑,以及如何自定义图片解析逻辑。

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打包结果:

iOS会按照项目结构输出图片资源到我们制定的目录 assets 下。

android中,drawable-mdpidrawable-xhdpidrawable-xxhdpi等存放不同分辨率屏幕下的图片,文件名的组成是目录和图片名称通过 _ 拼接。

2. 图片链接生成逻辑

代码位于react-native/Libraries/Image/resolveAssetSource

resolveAssetSource.js最终会 export 以下内容:

module.exports = resolveAssetSource;
module.exports.pickScale = AssetSourceResolver.pickScale;
module.exports.setCustomSourceTransformer = setCustomSourceTransformer;
  • resolveAssetSource: 图片地址拼接工具
  • pickScale: 像素比工具
  • setCustomSourceTransformer: 自定义图片链接处理方式

这里的重点是 resolveAssetSource,它会处理Imagesource,并返回图片地址。

创建了AssetSourceResolver,并传入getDevServerURL()getScriptURL()asset

如果存在自定义处理函数 _customSourceTransformer,就返回它的执行结果。它的设置就是通过setCustomSourceTransformer 来完成的。

否则就调用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 图片资源那些事

正文完
 0