React Native 多環境配置

一般,App 開發、部署會涉及多個環境,譬如開發、測試、預發佈、生產等環境。爲了不打錯包,咱們須要使用科學的方法來切換環境。java

使用 Configuration 和 Scheme 來實現 iOS 工程的多環境配置

按照下圖指引,分別 Duplicate "Debug" Configuration 和 Duplicate "Release" Configuration,建立 Debug production 和 Release production 兩個 configuration。react

Configurations

Configurations

建立環境變量

以下圖所示,點擊 + 按鈕,添加用戶定義設置android

User defined

咱們添加 BUILD_TYPE 和 ENVIRONMENT 這兩個自定義設置,結果如圖所示:ios

user environment

打開 Info 選項卡git

info_app_settings

添加一個名爲 AppSettings 的字典,它有兩個字段:BUILD_TYPEENVIRONMENT,它們的值分別爲 $(BUILD_TYPE)$(ENVIRONMENT)github

info_appsettings

點擊 Scheme 按鈕,建立新的 scheme,命名爲 MyApp qa,中間有個空格,這個 scheme 純粹是對 MyApp 的複製shell

點擊 Scheme 按鈕,建立新的 scheme,命名爲 MyApp production,中間有個空格。react-native

scheme_new

編輯 MyApp production, 把左邊 Run Test Profile Analyze Archiv 每一個選項卡中的 Build Configuration 設置爲對應的 production 版本。app

scheme_configuration

建立原生模塊

如今,咱們經過切換 scheme,就能切換 BUILD_TYPE 和 ENVIRONMENT 這些變量的值。爲了讓 RN 可以知道這些值,咱們須要藉助原生模塊ide

選中 MyApp 目錄,右鍵打開菜單,選擇 New File...

ios_new_file

在彈出的界面中,肯定 Cocoa Touch Classs 處於選中狀態,點擊 Next

ios_cocoa_touch

建立一個名爲 AppIno(名字隨意) 的類,而後 Next

ios_class_name

選擇類文件要存放的目錄,在這個示例工程裏,咱們把它放在 MyApp 目錄下

ios_folder

點擊 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 兩個環境,咱們經過原生模塊將相關環境變量導出。

建立原生模塊

打開 AndroidStudio,如圖所示,建立一個名爲 AppInfo 的 java 文件

android_create_class

android_create_class_2

編輯該文件

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 的文件

android_create_interface

編輯以下:

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
複製代碼

就這樣,咱們讀取到了原生模塊導出的變量,能夠在有須要的地方使用這些變量

如何切換環境

如今,咱們有了 qa 和 production 兩個環境,那麼如何切換環境呢?

iOS 在點擊 Run 按鈕以前,選擇對應環境的 scheme 便可,譬如選擇 MyApp qa 就是選擇了 qa 環境。

Android 則經過 ./gradlew assembleQaRelease./gradlew assembleProductionRelease 分別能夠打 qa 和 production 環境的包。

不一樣環境還能夠配置不一樣的 App icon 和 App name,有興趣的同窗能夠去研究下。

切換環境如此容易,還擔憂打錯包嗎?


本文是 React Native 工程化實踐 系列文章中的一篇

相關文章
相關標籤/搜索