首页教程React Native

多环境配置

多环境配置

通常,App 开发、部署会涉及多个环境,譬如开发、测试、预发布、生产等环境。为了避免打错包,我们需要使用科学的方法来切换环境。

使用 Configuration 和 Scheme 来实现 iOS 工程的多环境配置

按照下图指引,分别 Duplicate "Debug" Configuration 和 Duplicate "Release" Configuration,创建 Debug production 和 Release production 两个 configuration。

env-2021-10-19-16-12-15

env-2021-10-19-16-12-36

添加用户定义设置

如下图所示,点击 + 按钮,添加用户定义设置

env-2021-10-19-16-13-17

我们添加 BUILD_TYPE 和 ENVIRONMENT 这两个自定义设置,结果如图所示:

env-2021-10-19-16-12-59

打开 Info 选项卡

env-2021-10-19-16-14-16

添加一个名为 AppSettings 的字典,它有两个字段:BUILD_TYPE 和 ENVIRONMENT,它们的值分别为 $(BUILD_TYPE) 和 $(ENVIRONMENT)

env-2021-10-19-16-14-36

点击 Scheme 按钮,创建新的 Scheme,命名为 MyApp qa ,中间有个空格,这个 Scheme 纯粹是对 MyApp 的复制

点击 Scheme 按钮,创建新的 Scheme,命名为 MyApp production ,中间有个空格。

env-2021-10-19-16-14-54

编辑 MyApp production, 把左边 Run Test Profile Analyze Archiv 每个选项卡中的 Build Configuration 设置为对应的 production 版本。

env-2021-10-19-16-15-25

修改 ios/Podfile 文件

platform :ios, '9.0' + project 'MyApp', 'Debug' => :debug, 'productionDebug' => :debug

创建原生模块

现在,我们通过切换 Scheme,就能切换 BUILD_TYPE 和 ENVIRONMENT 这些变量的值。为了让 RN 能够知道这些值,我们需要借助原生模块open in new window。

选中 MyApp 目录,右键打开菜单,选择 New File...

env-2021-10-19-16-15-45

在弹出的界面中,确定 Cocoa Touch Classs 处于选中状态,点击 Nex

env-2021-10-19-16-16-08

创建一个名为 AppIno(名字随意) 的类,然后 Next

env-2021-10-19-16-16-34

选择类文件要存放的目录,在这个示例工程里,我们把它放在 MyApp 目录下

env-2021-10-19-16-16-55

点击 Create 完成创建

编辑原生模块

编辑 AppInfo.h 文件

#import <React/RCTBridge.h> NS_ASSUME_NONNULL_BEGIN @interface AppInfo : NSObject <RCTBridgeModule> @end NS_ASSUME_NONNULL_END

编辑 AppInfo.m 文件

#import "AppInfo.h" @implementation AppInfo RCT_EXPORT_MODULE(AppInfo) + (BOOL)requiresMainQueueSetup { return YES; } - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } - (NSDictionary *)constantsToExport { NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; NSMutableDictionary *settings = [[info objectForKey:@"AppSettings"] mutableCopy]; NSString *versionName = [info objectForKey:@"CFBundleShortVersionString"]; NSNumber *versionCode = [info objectForKey:@"CFBundleVersion"]; NSString *bundleId = [info objectForKey:@"CFBundleIdentifier"]; [settings setObject:versionName forKey:@"VERSION_NAME"]; [settings setObject:versionCode forKey:@"VERSION_CODE"]; [settings setObject:bundleId forKey:@"APPLICATION_ID"]; return settings; } @end

在上面这个原生模块中,我们导出了 BUILD_TYPE ENVIRONMENT VERSION_NAME VERSION_CODE APPLICATION_ID 等变量,这些变量,我们稍后可以在 RN 模块中读取。

使用 Flavor 来实现 Android 工程的多环境配置

添加 Flavor

编辑 android/app/build.gradle 文件,添加 Flavor

android{ flavorDimensions "default" productFlavors { qa { } production { } } }

就这样,我们创建了 qa 和 production 两个环境,我们通过原生模块open in new window将相关环境变量导出。

创建原生模块

打开 AndroidStudio,如图所示,创建一个名为 AppInfo 的 java 文件

env-2021-10-19-16-17-18

env-2021-10-19-16-17-36

编辑该文件

package com.myapp; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class AppInfo extends ReactContextBaseJavaModule { public AppInfo(@Nonnull ReactApplicationContext reactContext) { super(reactContext); } @Nonnull @Override public String getName() { return "AppInfo"; } @Nullable @Override public Map<String, Object> getConstants() { HashMap<String, Object> constants = new HashMap<>(); constants.put("ENVIRONMENT", BuildConfig.FLAVOR); constants.put("VERSION_NAME", BuildConfig.VERSION_NAME); constants.put("VERSION_CODE", BuildConfig.VERSION_CODE); constants.put("APPLICATION_ID", BuildConfig.APPLICATION_ID); constants.put("BUILD_TYPE", BuildConfig.BUILD_TYPE); return constants; } }

在上面的文件中,我们通过名为 AppInfo 的模块,导出了 BUILD_TYPE ENVIRONMENT VERSION_NAME VERSION_CODE APPLICATION_ID 等变量。

如图,创建一个叫 AppPackage 的文件

env-2021-10-19-16-17-59

编辑如下:

package com.myapp; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; public class AppPackage implements ReactPackage { @Nonnull @Override public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new AppInfo(reactContext)); return modules; } @Nonnull @Override public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) { return Collections.emptyList(); } }

修改 MainApplication.java 文件

package com.myapp; import android.app.Application; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import java.util.Arrays; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), // 添加本项目需要导出的 package new AppPackage() ); } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } }

到目前为止,我们就已经创建了原生模块,并导出了相关变量。

在 React Native 代码中读取原生代码导出的环境变量

在项目根目录下创建名为 app 的文件夹,在里面创建名为 AppInfo.ts 的文件,编辑如下

// AppInfo.ts import { NativeModules } from 'react-native' const AppInfo = NativeModules.AppInfo export const ENVIRONMENT: string = AppInfo.ENVIRONMENT export const VERSION_NAME: string = AppInfo.VERSION_NAME export const VERSION_CODE: number = AppInfo.VERSION_CODE export const APPLICATION_ID: string = AppInfo.APPLICATION_ID export const BUILD_TYPE_DEBUG = 'debug' export const BUILD_TYPE_RELEASE = 'release' export type BUILD_TYPE_DEBUG = typeof BUILD_TYPE_DEBUG export type BUILD_TYPE_RELEASE = typeof BUILD_TYPE_RELEASE export const BUILD_TYPE: BUILD_TYPE_DEBUG | BUILD_TYPE_RELEASE = AppInfo.BUILD_TYPE

就这样,我们读取到了原生模块导出的变量,可以在有需要的地方使用这些变量

如何切换环境

不同环境还可以配置不同的应用图标和应用名称,有兴趣的同学可以去研究下。

开发时,如何切换环境呢?

可以通过 IDE(Xcode 或 Android Studio) 或命令行的方式进行切换

通过 IDE 的方式切换环境

通过 Xcode 切换环境并运行应用

在 Xcode 左上角,点击 Scheme 按钮,选择对应环境的 Scheme 即可,譬如选择 MyApp qa 就是选择了 qa 环境。

点击 Xcode 的运行按钮,即可安装和启动 iOS App。

通过 Android Studio 切换环境并运行应用

在 Android Studio 左下角,找到并打开 Build Variants 选项卡,将 app module 的 Active Build Variant 切换至期待的环境即可。

点击 Android Studio 上的运行按钮,即可安装和启动 Android App。

通过命令行的方式切换环境

修改 package.json 文件

"scripts": { "android": "react-native run-android --variant qaDebug", "ios": "react-native run-ios --scheme 'MyApp qa' --configuration 'Debug'", "start": "react-native start --reset-cache", },

运行 npm run android 即可运行 qa 环境的可调试的 Android 应用

运行 npm run ios 即可运行 qa 环境的可调式 iOS 应用

可以通过以下方式运行 iOS 应用

npx react-native run-ios --scheme '对应环境的 scheme' --configuration '对应环境的 configuration'

譬如,以下命令,运行生产环境的可调式 iOS 应用

npx react-native run-ios --scheme 'MyApp production' --configuration 'Debug production'

如果想要真机调试,先要安装 ios-deploy

npm install -g ios-deploy

然后执行以下命令

npx react-native run-ios --scheme '对应环境的 scheme' --configuration '对应环境的 configuration' --device '你的 iPhone 名称'

譬如,以下命令,在一台名叫 zz 的 iPhone 上运行生产环境的可调试 iOS 应用

npx react-native run-ios --scheme 'MyApp production' --configuration 'Debug production' --device 'zz'

可以通过以下方式运行 Android 应用

npx react-native run-android --variant 构建变体

譬如,以下命令,运行 qa 环境的可调试 Android 应用

npx react-native run-android --variant qaDebug

又譬如,以下命令,运行生产环境的可调试 Android 应用

npx react-native run-android --variant productionDebug

如果需要真机调试,在通过数据线连接手机和电脑后

运行一次以下命令

adb reverse tcp:8081 tcp:8081

接下来再运行以下命令即可

npx react-native run-android --variant 构建变体

切换环境如此容易,还担心打错包吗?