进阶 reactnative.dev 2026-10-11 06:04:21 · 24 阅读
第54章 React Native 图片管理指南
版本:0.77
图片
静态图片资源
React Native 为 Android 和 iOS 应用提供了一套统一的图片及其他媒体资源管理方式。要在应用中添加静态图片,只需把图片放到源代码目录的任意位置,然后这样引用它:
React TSX
图片名的解析方式和 JS 模块一样。在上面的例子中,bundler 会在引用该图片的组件所在文件夹中查找 my-icon.png。
你可以使用 @2x 和 @3x 后缀来为不同屏幕密度提供图片。假如你有如下文件结构:
.
├── button.js
└── img
├── check.png
├── check@2x.png
└── check@3x.png
而 button.js 中的代码是:
React TSX
那么 bundler 会根据设备的屏幕密度打包并提供对应的图片。例如,iPhone 7 会使用 check@2x.png,而 iPhone 7 Plus 或 Nexus 5 会使用 check@3x.png。如果没有与屏幕密度完全匹配的图片,则会选择最接近的最佳选项。
在 Windows 上,向项目中添加新图片后,可能需要重启 bundler。
这种方式有以下优点:
- Android 和 iOS 使用同一套系统。
- 图片和 JavaScript 代码放在同一个文件夹里,组件自成一体。
- 没有全局命名空间,不用担心命名冲突。
- 只有实际用到的图片才会被打包进应用。
- 添加和修改图片无需重新编译应用,像平时一样刷新模拟器即可。
- bundler 能自动识别图片尺寸,无需在代码中重复声明。
- 图片可以通过 npm 包分发。
要让这套机制正常工作,require 中的图片名必须是静态可知的。
React TSX
// 正确
;
// 错误
const icon = this.props.active
? 'my-icon-active'
: 'my-icon-inactive';
;
// 正确
const icon = this.props.active
? require('./my-icon-active.png')
: require('./my-icon-inactive.png');
;
注意,通过这种方式引用的图片资源会自带尺寸(宽高)信息。如果你需要动态缩放图片(比如通过 flex),可能需要在 style 上手动设置 {width: undefined, height: undefined}。
静态非图片资源
上文提到的 require 语法同样可以把音频、视频或文档文件静态打包进项目。支持大多数常见文件类型,包括 .mp3、.wav、.mp4、.mov、.html 和 .pdf,完整列表见 bundler 默认配置。
如果需要支持其他类型,可以在 Metro 配置中添加 assetExts resolver 选项。
有个需要注意的地方:视频必须使用绝对定位而不是 flexGrow,因为目前非图片资源不会传递尺寸信息。对于直接通过 Xcode 或 Android 的 Assets 文件夹链接的视频,则不存在这个限制。
来自混合应用资源的图片
如果你在开发混合应用(部分 UI 用 React Native,部分 UI 用平台原生代码),仍然可以使用已打包进应用的图片。
对于通过 Xcode asset catalogs 或 Android drawable 文件夹包含的图片,使用不带扩展名的图片名:
React TSX source={{uri: 'app_icon'}}
style={{width: 40, height: 40}}
/>
对于 Android assets 文件夹中的图片,使用 asset:/ 协议: React TSX source={{uri: 'asset:/app_icon.png'}}
style={{width: 40, height: 40}}
/>
这两种方式都没有安全检查,你需要自行确保图片在应用中存在,并且必须手动指定图片尺寸。 网络图片 应用中显示的很多图片在编译时并不可用,或者为了控制安装包体积,你需要动态加载图片。与静态资源不同,网络图片需要手动指定尺寸。强烈建议使用 https,以满足 iOS 上 App Transport Security 的要求。 React TSX// GOOD
style={{width: 400, height: 400}} />
// BAD
图片的网络请求 如果想在图片请求中指定 HTTP 方法、请求头或请求体等,可以通过在 source 对象上定义这些属性来实现: React TSX source={{
uri: 'https://reactjs.org/logo-og.png',
method: 'POST',
headers: {
Pragma: 'no-cache',
},
body: 'Your Body goes here',
}}
style={{width: 400, height: 400}}
/>
URI Data 图片 有时你可能需要展示 REST API 返回的编码图片数据,这时可以使用 'data:' URI scheme。和网络图片一样,需要手动指定图片的尺寸。 info这种方式只推荐用于非常小且动态变化的图片,比如列表里来自数据库的图标。 React TSX// 至少要包含 width 和 height!
style={{
width: 51,
height: 51,
resizeMode: 'contain',
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==',
}}
/>
缓存控制 某些场景下,你可能希望只有图片已存在于本地缓存时才显示它,比如在网络图标加载出来之前,先显示低分辨率的占位图。另一些场景下,你可能不在乎图片是否过期,宁可显示旧图来节省带宽。cache 属性可以控制网络层与缓存的交互方式。 default:使用原生平台的默认缓存策略。 reload:总是从原始来源加载数据,不使用已有的缓存数据来响应该 URL 请求。 force-cache:无论缓存数据的新旧或过期与否,都优先使用已有缓存来响应请求;如果缓存中没有对应数据,再从原始来源加载。 only-if-cached:无论缓存数据的新旧或过期与否,都使用已有缓存来响应请求;如果缓存中没有对应数据,不会尝试从原始来源加载,该次加载视为失败。 React TSX source={{
uri: 'https://reactjs.org/logo-og.png',
cache: 'only-if-cached',
}}
style={{width: 400, height: 400}}
/>
本地文件系统图片 如果需要使用 Images.xcassets 之外的本地资源,可参考 CameraRoll 的示例。 选取相册中最合适的图片 iOS 会为相册中的同一张图片保存多个尺寸。出于性能考虑,务必选择与目标尺寸最接近的那一个:显示 200x200 的缩略图时,显然不应该用 3264x2448 的全尺寸原图作为 source。如果存在尺寸完全匹配的版本,React Native 会直接选用;否则会选用至少比目标大 50% 的第一个版本,以避免尺寸相近时缩放导致模糊。这些处理都是默认完成的,你无需自己编写那些繁琐且容易出错的代码。 为什么不自动为所有图片设定尺寸? 在浏览器里,如果不给图片指定尺寸,浏览器会先渲染一个 0x0 的元素,等图片下载完成后再按实际尺寸重新渲染。这种做法最大的问题是:图片加载过程中 UI 会不停跳动,用户体验非常糟糕。这种现象被称为累积布局偏移(Cumulative Layout Shift)。 React Native 有意不采用这种方式。开发者需要提前知道远程图片的尺寸(或宽高比),确实要多做一些工作,但我们相信这能带来更好的用户体验。而通过 require('./my-icon.png') 语法从应用包加载的静态图片则可以自动设定尺寸,因为在挂载时其尺寸信息是立即可用的。 例如,require('./my-icon.png') 的返回结果可能是: React TSX{"__packager_asset":true,"uri":"my-icon.png","width":591,"height":573}
source 以对象形式传入 React Native 中有一个有意思的设计:src 属性被命名为 source,并且接受的不是一个字符串,而是一个包含 uri 属性的对象。 React TSX
在基础设施层面,这样做的好处是可以在这个对象上附加元数据。例如使用 require('./my-icon.png') 时,我们会添加图片实际位置和尺寸的信息(不要依赖这个细节,未来可能会变!)。这同时也是为了面向未来:比如某天我们想支持 sprite,就不必只输出 {uri: ...},而是可以输出 {uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}},从而在所有现有调用点透明地支持 sprite。 从用户角度看,这个对象让你可以添加一些有用的属性,比如图片尺寸,以便计算显示时的大小。你也可以放心地把它当作自己的数据结构,用来存储更多图片相关信息。 ## 通过嵌套实现背景图 熟悉 Web 开发的开发者经常提出的一个需求是 background-image。针对这种场景,可以使用 组件,它的 props 与 相同,你可以把想叠加在上层的内容作为子元素放进去。
某些情况下你可能不想使用 ,因为它的实现比较简单。具体可参考 的文档,必要时可以自己封装自定义组件。
```tsx
return (
Inside
);
```
注意,你必须指定 width 和 height 样式属性。
## iOS 圆角样式
请注意,以下针对特定角的圆角样式属性可能会被 iOS 的图片组件忽略:
- borderTopLeftRadius
- borderTopRightRadius
- borderBottomLeftRadius
- borderBottomRightRadius
## 线程外解码
图片解码可能耗时超过一帧的时间。这是网页上掉帧的主要原因之一,因为解码是在主线程完成的。而在 React Native 中,图片解码是在另一个线程进行的。实际上,你本来就需要处理图片尚未下载完成的情况,所以在解码期间多显示几帧占位图并不需要修改任何代码。
## 配置 iOS 图片缓存上限
在 iOS 上,我们提供了一个 API 来覆盖 React Native 默认的图片缓存上限。这个调用应放在原生 AppDelegate 代码中(例如 didFinishLaunchingWithOptions 里)。
```objc
RCTSetImageCacheLimits(4*1024*1024, 200*1024*1024);
```
参数:
| 名称 | 类型 | 是否必填 | 说明 |
|------|------|----------|------|
| imageSizeLimit | number | 是 | 图片缓存大小上限 |
| totalCostLimit | number | 是 | 总缓存成本上限 |
在上面的代码示例中,图片大小上限设为 4 MB,总成本上限设为 200 MB。
style={{width: 40, height: 40}}
/>
对于 Android assets 文件夹中的图片,使用 asset:/ 协议: React TSX
style={{width: 40, height: 40}}
/>
这两种方式都没有安全检查,你需要自行确保图片在应用中存在,并且必须手动指定图片尺寸。 网络图片 应用中显示的很多图片在编译时并不可用,或者为了控制安装包体积,你需要动态加载图片。与静态资源不同,网络图片需要手动指定尺寸。强烈建议使用 https,以满足 iOS 上 App Transport Security 的要求。 React TSX// GOOD
// BAD
图片的网络请求 如果想在图片请求中指定 HTTP 方法、请求头或请求体等,可以通过在 source 对象上定义这些属性来实现: React TSX
uri: 'https://reactjs.org/logo-og.png',
method: 'POST',
headers: {
Pragma: 'no-cache',
},
body: 'Your Body goes here',
}}
style={{width: 400, height: 400}}
/>
URI Data 图片 有时你可能需要展示 REST API 返回的编码图片数据,这时可以使用 'data:' URI scheme。和网络图片一样,需要手动指定图片的尺寸。 info这种方式只推荐用于非常小且动态变化的图片,比如列表里来自数据库的图标。 React TSX// 至少要包含 width 和 height!
width: 51,
height: 51,
resizeMode: 'contain',
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==',
}}
/>
缓存控制 某些场景下,你可能希望只有图片已存在于本地缓存时才显示它,比如在网络图标加载出来之前,先显示低分辨率的占位图。另一些场景下,你可能不在乎图片是否过期,宁可显示旧图来节省带宽。cache 属性可以控制网络层与缓存的交互方式。 default:使用原生平台的默认缓存策略。 reload:总是从原始来源加载数据,不使用已有的缓存数据来响应该 URL 请求。 force-cache:无论缓存数据的新旧或过期与否,都优先使用已有缓存来响应请求;如果缓存中没有对应数据,再从原始来源加载。 only-if-cached:无论缓存数据的新旧或过期与否,都使用已有缓存来响应请求;如果缓存中没有对应数据,不会尝试从原始来源加载,该次加载视为失败。 React TSX
uri: 'https://reactjs.org/logo-og.png',
cache: 'only-if-cached',
}}
style={{width: 400, height: 400}}
/>
本地文件系统图片 如果需要使用 Images.xcassets 之外的本地资源,可参考 CameraRoll 的示例。 选取相册中最合适的图片 iOS 会为相册中的同一张图片保存多个尺寸。出于性能考虑,务必选择与目标尺寸最接近的那一个:显示 200x200 的缩略图时,显然不应该用 3264x2448 的全尺寸原图作为 source。如果存在尺寸完全匹配的版本,React Native 会直接选用;否则会选用至少比目标大 50% 的第一个版本,以避免尺寸相近时缩放导致模糊。这些处理都是默认完成的,你无需自己编写那些繁琐且容易出错的代码。 为什么不自动为所有图片设定尺寸? 在浏览器里,如果不给图片指定尺寸,浏览器会先渲染一个 0x0 的元素,等图片下载完成后再按实际尺寸重新渲染。这种做法最大的问题是:图片加载过程中 UI 会不停跳动,用户体验非常糟糕。这种现象被称为累积布局偏移(Cumulative Layout Shift)。 React Native 有意不采用这种方式。开发者需要提前知道远程图片的尺寸(或宽高比),确实要多做一些工作,但我们相信这能带来更好的用户体验。而通过 require('./my-icon.png') 语法从应用包加载的静态图片则可以自动设定尺寸,因为在挂载时其尺寸信息是立即可用的。 例如,require('./my-icon.png') 的返回结果可能是: React TSX{"__packager_asset":true,"uri":"my-icon.png","width":591,"height":573}
source 以对象形式传入 React Native 中有一个有意思的设计:src 属性被命名为 source,并且接受的不是一个字符串,而是一个包含 uri 属性的对象。 React TSX
在基础设施层面,这样做的好处是可以在这个对象上附加元数据。例如使用 require('./my-icon.png') 时,我们会添加图片实际位置和尺寸的信息(不要依赖这个细节,未来可能会变!)。这同时也是为了面向未来:比如某天我们想支持 sprite,就不必只输出 {uri: ...},而是可以输出 {uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}},从而在所有现有调用点透明地支持 sprite。 从用户角度看,这个对象让你可以添加一些有用的属性,比如图片尺寸,以便计算显示时的大小。你也可以放心地把它当作自己的数据结构,用来存储更多图片相关信息。 ## 通过嵌套实现背景图 熟悉 Web 开发的开发者经常提出的一个需求是 background-image。针对这种场景,可以使用