React Native:根据不同applicationId变体条件导入图片
针对你的需求,我们可以利用Gradle编译时变量和Android资源系统/Metro打包配置来实现,确保生产构建只包含当前变体的图片资源。下面是具体方案:
一、核心编译时变量(Android端自动生成)
Gradle在构建不同productFlavor时,会自动生成BuildConfig.FLAVOR变量,它的值就是当前变体的名称(比如你的main或aba)。这是最直接的编译时变量,不需要额外配置就能在原生代码中使用。
如果你需要自定义变量,也可以在productFlavors中添加buildConfigField来定义:
productFlavors { main { // 自定义编译时变量(可选,BuildConfig.FLAVOR已经够用) buildConfigField "String", "APP_VARIANT", "\"main\"" } aba { applicationIdSuffix ".aba" manifestPlaceholders = [appName: "ABA PRODUCT"] buildConfigField "String", "APP_VARIANT", "\"aba\"" } }
二、方案1:利用Android原生资源系统(推荐,确保资源不冗余)
这种方式完全依赖Android的资源合并机制,编译时只会打包当前变体的资源,不会包含其他变体的图片:
1. 按变体组织图片资源
在你的Android项目app/src目录下,创建对应变体的资源文件夹:
src/main/res/drawable-xxx:存放main变体的图片(比如logo.png)src/aba/res/drawable-xxx:存放aba变体的图片(可使用相同命名,或自定义名称)
Gradle编译时,会优先使用当前变体目录下的资源,若找不到则 fallback 到main目录的公共资源。
2. 在React Native中引用原生图片
在React Native代码中,直接引用原生资源即可,无需判断变体——Android会自动加载当前变体对应的图片:
// 引用原生drawable目录下的logo.png import logo from '@drawable/logo'; // 使用图片 <Image source={logo} />
如果需要根据变体动态选择不同名称的图片,可以通过原生模块暴露BuildConfig.FLAVOR变量到React Native:
步骤1:创建原生模块(Android)
在com.myapp包下创建VariantModule.java:
package com.myapp; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; public class VariantModule extends ReactContextBaseJavaModule { public VariantModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "VariantModule"; } @ReactMethod public String getCurrentFlavor() { return BuildConfig.FLAVOR; } }
步骤2:注册模块
在MainApplication.java中注册该模块:
import com.myapp.VariantModule; // ... @Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new ReactPackage() { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new VariantModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }); return packages; }
步骤3:在React Native中使用
import { NativeModules } from 'react-native'; const { VariantModule } = NativeModules; // 获取当前变体 const currentFlavor = await VariantModule.getCurrentFlavor(); // 根据变体加载对应原生图片 const imageSource = currentFlavor === 'aba' ? require('@drawable/aba-logo') : require('@drawable/main-logo');
三、方案2:使用React Native Assets + Metro打包配置
如果更倾向于用React Native的assets目录管理图片,可以通过Metro的自定义配置,根据编译时变量排除不需要的资源:
1. 按变体命名图片资源
在React Native的assets/images目录下,给不同变体的图片加上前缀:
assets/images/main-logo.pngassets/images/aba-logo.png
2. 配置Metro根据变体排除资源
在项目根目录创建metro.config.js:
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const baseConfig = getDefaultConfig(__dirname); // 读取编译时环境变量(由Gradle打包脚本传递) const currentFlavor = process.env.REACT_NATIVE_FLAVOR; const customConfig = { resolver: { // 排除非当前变体的图片 blacklistRE: currentFlavor === 'aba' ? /assets\/images\/main-.*/ : /assets\/images\/aba-.*/, }, }; module.exports = mergeConfig(baseConfig, customConfig);
3. 在Gradle中传递环境变量
修改android/build.gradle,在构建时给Metro传递变体信息:
tasks.whenTaskAdded { task -> // 针对Release构建任务 if (task.name.startsWith('assemble') && task.name.contains('Release')) { task.doFirst { // 根据当前变体设置环境变量 def flavorName = task.name.split('Release')[0].toLowerCase() environment 'REACT_NATIVE_FLAVOR', flavorName } } }
4. 在React Native中动态加载图片
import { Platform, NativeModules } from 'react-native'; // 注意:这里使用条件require,Metro会根据配置排除未使用的图片 const getVariantImage = async () => { // 可以通过原生模块获取变体,或者直接使用环境变量 const flavor = Platform.OS === 'android' ? await NativeModules.VariantModule.getCurrentFlavor() : 'main'; // iOS可对应Scheme做类似处理 return flavor === 'aba' ? require('./assets/images/aba-logo.png') : require('./assets/images/main-logo.png'); };
关键注意事项
- 方案1的优势:完全利用Android原生资源系统,编译时自动过滤非当前变体的资源,最可靠且无需额外打包配置。
- 方案2的注意点:动态
require必须是静态可分析的(即条件判断不能是运行时动态值),否则Metro无法正确tree shaking,会打包所有图片。
内容的提问来源于stack exchange,提问作者user2078023

