欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

react native圖片解析流程詳解

 更新時間:2023年01月13日 11:02:41   作者:lcwlucky  
這篇文章主要為大家介紹了react native圖片解析流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

我們知道,在react-native中加載一張圖片需要使用Image組件,其有兩種使用方式

import bg from './bg.png';
// 1. 加載本地圖片資源
<Image source={bg}/>
<Image source={require('./bg.png)}/>
// 2. 加載網(wǎng)絡(luò)圖片資源
<Image source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}/>

1. 構(gòu)建輸出的產(chǎn)物

如果代碼里import了一張圖片

// src/index.js
import { Image } from 'react-native';
import bg from './bg.png';
const jsx = <Image source={bg}/>
.
└── src/
    ├── index.js
    ├── bg.png
    ├── bg@1.5x.png
    ├── bg@2x.png
    └── bg@3x.png

那么通過metro打包后圖片在js bundle中到底長啥樣呢? 先通過以下命令構(gòu)建bundle

// ios
react-native bundle --entry-file src/index.ts --platform ios --bundle-output dist/ios/ios.bundle.js --assets-dest dist/ios 
// android
react-native bundle --entry-file src/index.ts --platform android --bundle-output dist/android/android.bundle.js --assets-dest dist/android

構(gòu)建結(jié)果如下:

ios會將圖片輸出到assets目錄下,且圖片保留圖片目錄層次結(jié)構(gòu)。

android中,drawable-mdpi,drawable-hdpidrawable-xhdpi,drawable-xxhdpi文件夾存放不同分辨率屏幕下的圖片,文件名由目錄和圖片名稱通過_拼接組成。

drawable-mdpi: 1x

drawable-hdpi: 1.5x

drawable-xhdpi: 2x

drawable-xxhdpi: 3x

2. js bundle分析

打開ios.bundle.js,首先看一下bundle中的兩個重要的方法:

  • __d: 即define。 注冊一個模塊到全局modules中,且這個模塊的id是唯一的,大致源碼如下
 modules = Object.create(null);
 function define(factory, moduleId, dependencyMap) {
    if (modules[moduleId] != null) {
      return;
    }
    var mod = {
      dependencyMap: dependencyMap,
      factory: factory,
      hasError: false,
      importedAll: EMPTY,
      importedDefault: EMPTY,
      isInitialized: false,
      publicModule: {
        exports: {},
      },
    };
    modules[moduleId] = mod;
  }
  • __r: 即 metroRequire, 它接收一個模塊id作為參數(shù),也就是 __d 所注冊的模塊id,其調(diào)用了在 __d 中注冊的工廠方法。
function metroRequire(moduleId) {
    var moduleIdReallyIsNumber = moduleId;
    var module = modules[moduleIdReallyIsNumber];
    return module && module.isInitialized
      // 如果已經(jīng)初始化過,直接返回緩存
      ? module.publicModule.exports // 這里其實就是 module.exports
      // 如果沒有初始化過,則內(nèi)部調(diào)用module的factory方法初始化
      : guardedLoadModule(moduleIdReallyIsNumber, module);
  }

我們import的圖片最終生成了這樣一段代碼

__d(
  // factory
  function (
    global,
    _$$_REQUIRE, //__r
    _$$_IMPORT_DEFAULT,
    _$$_IMPORT_ALL,
    module,
    exports,
    _dependencyMap
  ) {
    module.exports = _$$_REQUIRE(
      _dependencyMap[0],
      'react-native/Libraries/Image/AssetRegistry'
    ).registerAsset({
      __packager_asset: true,
      httpServerLocation: '/assets/src',
      width: 295,
      height: 153,
      scales: [1, 1.5, 2, 3],
      hash: '615a107224f6f73b539078be1c162c6c',
      name: 'bg',
      type: 'png',
    });
  },
  479,
  [223], // 223 就是react-native/Libraries/Image/AssetRegistry 模塊
  'src/bg.png'
);

由代碼得知,我們在代碼中import的圖片被當做一個module進行處理,內(nèi)部調(diào)用了react-native提供的registerAsset方法來注冊資源。

資源信息包括了以下幾個重要字段:

  • httpServerLocation:圖片文件夾在http server中的地址。如果我們在本地開發(fā),metro內(nèi)部會啟動一個http server,這個字段就是告訴server圖片文件夾在哪。
  • scales:圖片有哪些尺寸。因為bg.png存在 1x,1.5x,2x,3x 4種尺寸,所以這里scales就為[1, 1.5, 2, 3]。如果你的圖片只有3x,那么scales就為 [3]。
  • type: 圖片后綴。
  • width:圖片寬度
  • height:圖片高度

經(jīng)過測試發(fā)現(xiàn),圖片有哪些尺寸,始終都是1x圖的寬高。比如一張圖片只有3x尺寸,那么metro在打包時會通過當前3x圖的寬高計算出1x圖的寬高,但是scales仍為 [3]。

// react-native/Libraries/Image/AssetRegistry
__d(
  function (
    global,
    _$$_REQUIRE,
    _$$_IMPORT_DEFAULT,
    _$$_IMPORT_ALL,
    module,
    exports,
    _dependencyMap
  ) {
    'use strict';
    var assets = [];
    function registerAsset(asset) {
      return assets.push(asset);
    }
    function getAssetByID(assetId) {
      return assets[assetId - 1];
    }
    module.exports = {
      registerAsset: registerAsset,
      getAssetByID: getAssetByID,
    };
  },
  223,
  [],
  'node_modules/react-native/Libraries/Image/AssetRegistry.js'
);

在注冊圖片時會調(diào)用registerAsset方法,registerAsset將圖片module注冊到一個全局assets數(shù)組中,然后返回當前assets數(shù)組的長度,也表示圖片模塊id。getAssetByID 方法會根據(jù)傳入的id,從全局assets數(shù)組中取出已經(jīng)注冊的圖片信息。

需要注意這里的圖片信息只包含本地圖片資源,而不包含網(wǎng)絡(luò)圖片資源

所以我們在代碼中寫的import bg from './bg.png', 經(jīng)過打包后bg就是一個數(shù)字(模塊注冊時的assets.length)。因此<Image source={xxx}/>加載本地圖片資源時,source prop其實傳入的是一個數(shù)字。

3. 圖片source拼接

我們來看看Image組件是如何通過圖片模塊id來拼接source的

// react-native/Libraries/Image/Image.ios.js  代碼有刪減
const BaseImage = (props) => {
  const source = getImageSourcesFromImageProps(props) || {
    uri: undefined,
    width: undefined,
    height: undefined,
  };
  let sources;
  let style: ImageStyleProp;
  if (Array.isArray(source)) {
    style = flattenStyle([styles.base, props.style]) || {};
    sources = source;
  } else {
    const {width = props.width, height = props.height, uri} = source;
    style = flattenStyle([{width, height}, styles.base, props.style]) || {};
    sources = [source];
    if (uri === '') {
      console.warn('source.uri should not be an empty string');
    }
  }
  const objectFit =
    style && style.objectFit
      ? convertObjectFitToResizeMode(style.objectFit)
      : null;
  const resizeMode =
    objectFit || props.resizeMode || (style && style.resizeMode) || 'cover';
  const {
    height,
    width,
    ...restProps
  } = props;
  return (
     return (
       <ImageViewNativeComponent
          {...restProps}
          style={style}
          resizeMode={resizeMode}
          source={sources}
        />
     );
  );
};

Image組件一開始會調(diào)用getImageSourcesFromImageProps來解析傳入的source, 然后傳入到native提供的組件(RCTImageView)進而顯示。

// 代碼有刪減
function getImageSourcesFromImageProps(imageProps) {
  return resolveAssetSource(imageProps.source);
}

進入resolveAssetSource

/**
 * `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,
  );
  // 如果存在自定義處理函數(shù)_customSourceTransformer,就返回它的執(zhí)行結(jié)果。
  // 可以通過setCustomSourceTransformer來設(shè)置。
  if (_customSourceTransformer) {
    return _customSourceTransformer(resolver);
  }
  return resolver.defaultAsset();
}

通過注釋和源碼得知,傳入的source有兩種形式:

  • object形式,包含一個uri網(wǎng)絡(luò)圖片地址
  • 數(shù)字形式,即上文所說在AssetRegistry中注冊返回的模塊id

如果是source是一個object直接返回。否則會通過AssetRegistry.getAssetByID將之前注冊的圖片信息提取出來,然后經(jīng)過AssetSourceResolver解析,形成最終的source并返回。

而在初始化AssetSourceResolver時傳入了三個參數(shù),分別是服務(wù)器地址(類似 http://localhost:8081) ,bundle所在位置和注冊的圖片信息。

defaultAsset包含了最終返回source的邏輯

// 代碼有刪減
class AssetSourceResolver {
  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 {
    // 如果是本地開發(fā)
    if (this.isLoadedFromServer()) {
      return this.assetServerURL();
    }
    // 非本地開發(fā),Native內(nèi)嵌
    if (Platform.OS === 'android') {
      return this.isLoadedFromFileSystem()
        ? this.drawableFolderInBundle()
        : this.resourceIdentifierWithoutScale();
    } else {
      return this.scaledAssetURLNearBundle();
    }
  }
  assetServerURL(): ResolvedAssetSource {
    return this.fromSource(
      this.serverUrl +
        getScaledAssetPath(this.asset) +
        '?platform=' +
        Platform.OS +
        '&hash=' +
        this.asset.hash,
    );
  }
  /**
   * If the jsbundle is running from a sideload location, this resolves assets
   * relative to its location
   * E.g. 'file:///sdcard/xxx/drawable-xxhdpi/src_bg.png'
   */
  drawableFolderInBundle(): ResolvedAssetSource {
    const path = this.jsbundleUrl || 'file://';
    return this.fromSource(path + getAssetPathInDrawableFolder(this.asset));
  }
   /**
   * The default location of assets bundled with the app, located by
   * resource identifier
   * The Android resource system picks the correct scale.
   * E.g. 'src_bg'
   */
  resourceIdentifierWithoutScale(): ResolvedAssetSource {
    return this.fromSource(getAndroidResourceIdentifier(this.asset));
  }
   /**
   * Resolves to where the bundle is running from, with a scaled asset filename
   * E.g. 'file:///sdcard/bundle/assets/src/bg@3x.png'
   */
  scaledAssetURLNearBundle(): ResolvedAssetSource {
    const path = this.jsbundleUrl || 'file://';
    return this.fromSource(
      // Assets can have relative paths outside of the project root.
      // When bundling them we replace `../` with `_` to make sure they
      // don't end up outside of the expected assets directory.
      path + getScaledAssetPath(this.asset).replace(/\.\.\//g, '_')
    );
  }
  fromSource(source: string): ResolvedAssetSource {
    return {
      __packager_asset: true,
      width: this.asset.width,
      height: this.asset.height,
      uri: source,
      scale: pickScale(this.asset.scales, PixelRatio.get()),
    };
  }
}
/**
 * 返回圖片在服務(wù)器中的路徑,比如 'assets/src/bg@3x.png'
 */
function getScaledAssetPath(asset: PackagerAsset): string {
  const scale = pickScale(asset.scales, PixelRatio.get());
  const scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
  // 這里的assetDir其實就是 之前通過__d定義的圖片信息中的httpServerLocation,即assets_src
  const assetDir = getBasePath(asset); 
  return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type;
}
// 判斷選擇哪種尺寸的圖片
// RN根據(jù)當前手機的ratio加載對應(yīng)的scale圖片。如果當前手機的ratio沒有匹配到正確的scale圖片,則會獲取第一個大于當前手機ratio的scale圖片。
// 例如當前手機的scale為2,如果存在2x圖片,則返回2x圖片。如果沒有2x圖,則會向上獲取3x圖
export function pickScale(scales: Array<number>, deviceScale?: number): number {
  if (deviceScale == null) {
    deviceScale = PixelRatio.get();
  }
  // Packager guarantees that `scales` array is sorted
  for (let i = 0; i < scales.length; i++) {
    if (scales[i] >= deviceScale) {
      return scales[i];
    }
  }
  // If nothing matches, device scale is larger than any available
  // scales, so we return the biggest one. Unless the array is empty,
  // in which case we default to 1
  return scales[scales.length - 1] || 1;
}

通過分析代碼可知有兩種情況:

3.1 如果bundle放在服務(wù)器(本地開發(fā))

圖片source由serverUrl + 圖片在服務(wù)器中的地址拼接組成

this.serverUrl +
        getScaledAssetPath(this.asset) +
        '?platform=' +
        Platform.OS +
        '&amp;hash=' +
        this.asset.hash,

比如上述的bg圖片在本地開發(fā)時會最終返回

http://localhost:8081/assets/src/bg@3x.png?platform=ios&hash=615a107224f6f73b539078be1c162c6c

3.2 bundle內(nèi)置在app中(app下載bundle和assets后執(zhí)行)

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

ios直接從文件系統(tǒng)讀取

android分為兩種:

  • 資源標識符(Android 資源系統(tǒng)會選擇正確的比例)
  • 文件系統(tǒng)

4. Image style的witdh和height沒有聲明會發(fā)生什么?

有時候在我們在Image組件中沒有傳入style,或者并沒有在style中聲明width和height,那么圖片實際展示的寬高為多少呢?

//image.ios.js
const source = getImageSourcesFromImageProps(props) || {
    uri: undefined,
    width: undefined,
    height: undefined,
  };
const {width, height, uri} = source;
style = flattenStyle([{width, height}, styles.base, props.style]) || {};

由Image組件源碼得知, 此時會使用注冊圖片模塊時的width和height。

registerAsset({
      __packager_asset: true,
      httpServerLocation: '/assets/src',
      width: 295,
      height: 153,
      scales: [1, 1.5, 2, 3],
      hash: '615a107224f6f73b539078be1c162c6c',
      name: 'bg',
      type: 'png',
    });

前面提到,無論圖片有哪些尺寸,注冊時的寬高始終是1x圖的寬高。所以當我們在Image組件沒有寫style 寬高時,RN會默認設(shè)置為1x圖的寬高(無論你的手機屏幕尺寸如何)。

以上就是react native圖片解析流程詳解的詳細內(nèi)容,更多關(guān)于react native圖片解析的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • reset.css瀏覽器默認樣式表重置(user?agent?stylesheet)的示例代碼

    reset.css瀏覽器默認樣式表重置(user?agent?stylesheet)的示例代碼

    這篇文章主要介紹了reset.css瀏覽器默認樣式表重置(user?agent?stylesheet),本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-12-12
  • React自定義Hook的實現(xiàn)

    React自定義Hook的實現(xiàn)

    自定義Hook是一種自定義函數(shù),它封裝了React Hook的邏輯,并通過命名約定來使其可重用,本文主要介紹了React自定義Hook的實現(xiàn),感興趣的可以了解一下
    2023-11-11
  • React在Dva項目中創(chuàng)建并引用頁面局部組件的方式

    React在Dva項目中創(chuàng)建并引用頁面局部組件的方式

    這篇文章主要介紹了React在Dva項目中創(chuàng)建并引用頁面局部組件的方式,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • react電商商品列表的實現(xiàn)流程詳解

    react電商商品列表的實現(xiàn)流程詳解

    這篇文章主要介紹了react實現(xiàn)電商商品列表的流程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • react?hooks閉包陷阱切入淺談

    react?hooks閉包陷阱切入淺談

    這篇文章主要介紹了從react?hooks閉包陷阱切入淺談react?hooks,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • react中使用echarts,并實現(xiàn)tooltip循環(huán)輪播方式

    react中使用echarts,并實現(xiàn)tooltip循環(huán)輪播方式

    這篇文章主要介紹了react中使用echarts,并實現(xiàn)tooltip循環(huán)輪播方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 詳解webpack2+node+react+babel實現(xiàn)熱加載(hmr)

    詳解webpack2+node+react+babel實現(xiàn)熱加載(hmr)

    這篇文章主要介紹了詳解webpack2+node+react+babel實現(xiàn)熱加載(hmr) ,非常具有實用價值,需要的朋友可以參考下
    2017-08-08
  • React詳細講解JSX和組件的使用

    React詳細講解JSX和組件的使用

    jsx就是javsscript與xml結(jié)合的一種格式,是js語法的一種擴展,只要把html代碼寫在js中就是jsx。react中定義組件有3種寫法:函數(shù)的方式、es5的寫法、es6(類)的寫法
    2022-08-08
  • ReactNative中使用Redux架構(gòu)總結(jié)

    ReactNative中使用Redux架構(gòu)總結(jié)

    本篇文章主要介紹了ReactNative中使用Redux架構(gòu)總結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • react native之ScrollView下拉刷新效果

    react native之ScrollView下拉刷新效果

    這篇文章主要為大家詳細介紹了react native之ScrollView下拉刷新效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評論