如何使用 FastImage 无侵入替换 React Native 的 Image
React Native 自带的 Image 在大量图片、复杂列表或需要更好缓存策略时,往往不如 react-native-fast-image 表现好。FastImage 基于 SDWebImage (iOS) 和 Glide (Android),提供更高效的缓存与加载。
若希望业务里继续写 import { Image } from 'react-native',而不必全局改成 import FastImage from 'react-native-fast-image',可采用 无侵入增强 React Native 组件方案 中的 Babel 别名 + react-native-proxy + Shim 方案:在 proxy 层把 Image 替换成基于 FastImage 的 ImageShim,对业务无侵入、类型也无需改动。
思路
- 用 Babel 的
module-resolver把react-native指到项目内的react-native-proxy.js。 - proxy 从真实
react-native做一层 Proxy,对Image返回我们实现的 ImageShim,其余组件照常透传。 - ImageShim 内部使用 FastImage 渲染,并做好与 RN
Image的 API 对齐:source、resizeMode、style,以及静态方法resolveAssetSource、prefetch、getSize等。
这样所有 import { Image } from 'react-native' 在运行时会自动变成 FastImage 能力,无需改业务代码。
1. 安装依赖
本方案依赖 Babel 的 module-resolver 插件做别名解析,以及 FastImage 库:
# 用于在 babel.config.js 中配置 react-native 别名
yarn add -D babel-plugin-module-resolver
# FastImage
yarn add react-native-fast-image
# 或使用 fork,例如:yarn add @d11/react-native-fast-image
按 react-native-fast-image 官方文档完成 iOS/Android 的 link 或 Pod 安装(如需要)。
2. Babel 配置
在 babel.config.js 的 module-resolver 里为 react-native 配置别名,指向项目中的 proxy 文件(路径按项目结构调整;若已为其他 Shim 配过可跳过):
// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
[
'module-resolver',
{
root: ['./'],
extensions: ['.ts', '.tsx', '.ios.js', '.android.js', '.js', '.json'],
alias: {
'^react-native$': './app/utils/react-native-proxy.js',
// ... 其他 alias
},
},
],
],
};
3. react-native-proxy.js 中代理 Image
在 proxy 里对 Image 返回 ImageShim,其它导出仍用真实 RN:
// app/utils/react-native-proxy.js
const RN = require('../../node_modules/react-native');
module.exports = new Proxy(RN, {
get(target, prop) {
if (prop === 'Image') {
if (!module.exports.__ImageShim) {
module.exports.__ImageShim = require('./shim/ImageShim').default;
}
return module.exports.__ImageShim;
}
// View、Text 等若已有 Shim 可在此一并代理
return target[prop];
},
});
4. ImageShim 实现
Shim 内用 FastImage 渲染,并做两件事:
- API 对齐:把 RN 的
resizeMode字符串(如'cover'、'contain')转成 FastImage 的resizeMode常量;对source为require(...)得到的数字时,用Image.resolveAssetSource取宽高并落到 style,避免本地资源显示异常。 - 静态方法透传:把
Image.resolveAssetSource、prefetch、getSize、getSizeWithHeaders、abortPrefetch等挂到 Shim 上,保证Image.resolveAssetSource(require('./x.png'))等用法在业务里仍然可用。
示例(以 react-native-fast-image 或兼容 API 的 fork 为例):
// app/utils/shim/ImageShim.tsx
import FastImage from 'react-native-fast-image'; // 或 @d11/react-native-fast-image
import React from 'react';
import type { ImageResizeMode } from 'react-native';
const RN = require('../../node_modules/react-native');
const ImageShim = (props: any) => {
const { style, source, resizeMode, ...rest } = props;
let flatStyle = RN.StyleSheet.flatten(style) || {};
if (typeof source === 'number') {
const { width, height } = RN.Image.resolveAssetSource(source);
if (width && height) {
flatStyle = { width, height, ...flatStyle };
}
}
return (
<FastImage
{...rest}
style={flatStyle}
source={source}
resizeMode={fastImageResizeMode(resizeMode)}
tintColor={flatStyle.tintColor}
/>
);
};
function fastImageResizeMode(mode: ImageResizeMode = 'cover') {
switch (mode) {
case 'contain':
return FastImage.resizeMode.contain;
case 'stretch':
return FastImage.resizeMode.stretch;
case 'center':
return FastImage.resizeMode.center;
case 'cover':
return FastImage.resizeMode.cover;
case 'repeat':
return FastImage.resizeMode.cover;
default:
return FastImage.resizeMode.cover;
}
}
ImageShim.resolveAssetSource = RN.Image.resolveAssetSource;
ImageShim.prefetch = RN.Image.prefetch;
ImageShim.getSize = RN.Image.getSize;
ImageShim.getSizeWithHeaders = RN.Image.getSizeWithHeaders;
ImageShim.abortPrefetch = RN.Image.abortPrefetch;
export default ImageShim;
要点:
- 本地资源:
source={require('./x.png')}时,用RN.Image.resolveAssetSource(source)拿到宽高并写入 style,避免 FastImage 下尺寸异常。 - resizeMode:FastImage 使用自己的常量,需从 RN 的字符串映射过去;
repeat一般用cover替代(按需可再细化)。 - 静态方法:保证
Image.resolveAssetSource、Image.prefetch等在业务里仍挂在「Image」上,即当前 Shim 组件。
小结
| 步骤 | 说明 |
|---|---|
| Babel alias | 将 react-native 指向 react-native-proxy.js |
| proxy | 对 Image 返回 ImageShim,其余透传 |
| ImageShim | 用 FastImage 渲染,对齐 source/resizeMode/style,并透传静态方法 |
业务侧继续 import { Image } from 'react-native' 即可,无需改成 FastImage,即可获得更好的缓存与加载表现。