进阶 reactnative.dev 2026-10-11 06:04:21 · 39 阅读
第58章 将 React Native 集成到现有应用中
版本:0.77 与现有应用集成
从零开始开发一个新的移动应用时,React Native 是绝佳选择。不过,它同样适合在现有原生应用中添加单个视图或用户流程。只需几步,你就能加入基于 React Native 的新功能、页面、视图等。
具体步骤因目标平台而异。
Android (Java & Kotlin)iOS (Objective-C and Swift)
核心概念
将 React Native 组件集成到 Android 应用中,关键在于以下几步:
搭建正确的目录结构。
安装必要的 NPM 依赖。
在 Gradle 配置中加入 React Native。
用 TypeScript 编写第一个 React Native 页面。
通过 ReactActivity 将 React Native 与你的 Android 代码集成。
运行 bundler 验证集成效果,实际查看应用的运行情况。
使用 Community Template
建议你在阅读本指南时参考 React Native Community Template。该模板包含一个最小化的 Android 应用,能帮助你理解如何将 React Native 集成到现有 Android 应用中。
前提条件
请先按照“搭建开发环境”和“不使用框架使用 React Native”的指南,配置好用于构建 Android React Native 应用的开发环境。
本指南还假设你熟悉 Android 开发的基础知识,比如创建 Activity 和编辑 AndroidManifest.xml 文件。
1. 搭建目录结构
为了让后续步骤顺利进行,请为集成了 React Native 的项目新建一个文件夹,然后把现有的 Android 项目移到 /android 子文件夹中。
2. 安装 NPM 依赖
进入根目录,运行以下命令:
curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.75-stable/template/package.json
这会把 Community template 中的 package.json 文件复制到你的项目里。 接着运行以下命令安装 NPM 依赖包: npmYarnnpm install
yarn install
安装过程会创建一个新的 node_modules 文件夹,用于存放构建项目所需的全部 JavaScript 依赖。 请将 node_modules/ 添加到 .gitignore 文件中(可参考社区的默认版本)。 3. 将 React Native 集成到应用中 配置 Gradle React Native 通过 React Native Gradle Plugin 来配置依赖和项目设置。 首先编辑 settings.gradle 文件,加入以下几行(参照社区模板的建议): Groovy// 配置用于自动链接的 React Native Gradle Settings 插件
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
// 如果使用 .gradle.kts 文件:
// extensions.configure { autolinkLibrariesFromCommand() }
includeBuild("../node_modules/@react-native/gradle-plugin")
// 在这里包含你现有的 Gradle 模块。
// include(":app")
然后打开顶层的 build.gradle,加入这一行(参照社区模板的建议): Diffbuildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
+ classpath("com.facebook.react:react-native-gradle-plugin")
}
}
这样可以确保 React Native Gradle Plugin(RNGP)在项目中可用。 最后,在应用的 build.gradle 文件中加入以下几行(注意这是另一个 build.gradle 文件,通常位于 app 文件夹内——可参考社区模板中的对应文件): Diffapply plugin: "com.android.application"
+apply plugin: "com.facebook.react"
repositories {
mavenCentral()
}
dependencies {
// 其他依赖放在这里
+ // 注意:这里故意不指定版本号,由 RNGP 自动处理。
+ // 如果不使用 RNGP,则需要手动指定版本。
+ implementation("com.facebook.react:react-android")
+ implementation("com.facebook.react:hermes-android")
}
+react {
+ // 启用 Autolinking 所需配置 - https://github.com/react-native-community/cli/blob/master/docs/autolinking.md
+ autolinkLibrariesWithApp()
+}
最后,打开你的应用 gradle.properties 文件,添加以下几行(可参考 Community 模板文件): Diff+reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
+newArchEnabled=true
+hermesEnabled=true
配置 Manifest 文件 首先,确保 AndroidManifest.xml 中声明了网络权限: Diff
+
android:name=".MainApplication">
然后,在 debug 的 AndroidManifest.xml 中启用明文传输: Diff
xmlns:tools="http://schemas.android.com/tools">
+ android:usesCleartextTraffic="true"
+ tools:targetApi="28"
/>
老规矩,Community 模板里的 AndroidManifest.xml 文件可供参考:main 和 debug 这一步是必须的,因为应用需要通过 HTTP 与本地的打包工具 Metro 通信。 注意:这些配置只能加在 debug 的 manifest 里。 4. 编写 TypeScript 代码 接下来我们正式修改原生 Android 应用,集成 React Native。 第一步是编写新页面的 React Native 代码,之后会把它集成到我们的应用里。 创建 index.js 文件 首先,在 React Native 项目的根目录下创建一个空的 index.js 文件。 index.js 是 React Native 应用的入口,必不可少。它既可以是一个很小的文件,只负责导入 React Native 组件或应用的其他代码,也可以直接包含全部所需代码。 我们的 index.js 如下所示(可参考 Community 模板文件): JavaScriptimport {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);
创建 App.tsx 文件 接着创建一个 App.tsx 文件。这是一个支持 JSX 语法的 TypeScript 文件,包含我们要集成到 Android 应用中的 React Native 根组件(链接): React TSXimport React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Here the Community template file as reference 5. 与现有 Android 代码集成 接下来我们需要添加一些原生代码,用来启动 React Native 运行时,并让它渲染我们的 React 组件。 更新你的 Application 类 首先,需要更新 Application 类,以正确初始化 React Native,如下所示: JavaKotlinDiffpackage;
import android.app.Application;
+import com.facebook.react.PackageList;
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactHost;
+import com.facebook.react.ReactNativeHost;
+import com.facebook.react.ReactPackage;
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
+import com.facebook.react.defaults.DefaultReactHost;
+import com.facebook.react.defaults.DefaultReactNativeHost;
+import com.facebook.soloader.SoLoader;
+import com.facebook.react.soloader.OpenSourceMergedSoMapping
+import java.util.List;
-class MainApplication extends Application {
+class MainApplication extends Application implements ReactApplication {
+ @Override
+ public ReactNativeHost getReactNativeHost() {
+ return new DefaultReactNativeHost(this) {
+ @Override
+ protected List getPackages() { return new PackageList(this).getPackages(); }
+ @Override
+ protected String getJSMainModuleName() { return "index"; }
+ @Override
+ public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; }
+ @Override
+ protected boolean isNewArchEnabled() { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; }
+ @Override
+ protected Boolean isHermesEnabled() { return BuildConfig.IS_HERMES_ENABLED; }
+ };
+ }
+ @Override
+ public ReactHost getReactHost() {
+ return DefaultReactHost.getDefaultReactHost(getApplicationContext(), getReactNativeHost());
+ }
@Override
public void onCreate() {
super.onCreate();
+ SoLoader.init(this, OpenSourceMergedSoMapping);
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ DefaultNewArchitectureEntryPoint.load();
+ }
}
}
Diff// package
import android.app.Application
+import com.facebook.react.PackageList
+import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
+import com.facebook.react.ReactNativeHost
+import com.facebook.react.ReactPackage
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
+import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
+import com.facebook.react.defaults.DefaultReactNativeHost
+import com.facebook.soloader.SoLoader
+import com.facebook.react.soloader.OpenSourceMergedSoMapping
-class MainApplication : Application() {
+class MainApplication : Application(), ReactApplication {
+ override val reactNativeHost: ReactNativeHost =
+ object : DefaultReactNativeHost(this) {
+ override fun getPackages(): List = PackageList(this).packages
+ override fun getJSMainModuleName(): String = "index"
+ override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
+ override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+ override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
+ }
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
+ SoLoader.init(this, OpenSourceMergedSoMapping)
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ load()
+ }
}
}
照例,这里提供 Community 模板中的 MainApplication.kt 文件作为参考 创建 ReactActivity 最后,我们需要创建一个新的 Activity,让它继承 ReactActivity 并承载 React Native 代码。这个 Activity 负责启动 React Native 运行时并渲染 React 组件。 JavaKotlinJava// package;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
public class MyReactActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "HelloWorld";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new DefaultReactActivityDelegate(this, getMainComponentName(), DefaultNewArchitectureEntryPoint.getFabricEnabled());
}
}
Kotlin// package
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
class MyReactActivity : ReactActivity() {
override fun getMainComponentName(): String = "HelloWorld"
override fun createReactActivityDelegate(): ReactActivityDelegate =
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}
照例,这里提供 Community 模板中的 MainActivity.kt 文件作为参考 每次创建新的 Activity,都需要把它注册到 AndroidManifest.xml 文件中。另外,还要把 MyReactActivity 的主题设置为 Theme.AppCompat.Light.NoActionBar(或其他不带 ActionBar 的主题),否则应用会在 React Native 界面上方显示一个 ActionBar: Diff
android:name=".MainApplication">
++ android:name=".MyReactActivity"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+
现在你的 Activity 已经可以运行 JavaScript 代码了。 6. 测试集成效果 到这里,集成 React Native 的所有基础步骤都已完成。接下来我们启动 Metro bundler,把 TypeScript 应用代码打包成 bundle。Metro 的 HTTP 服务器会在你的开发环境中通过 localhost 向模拟器或设备提供 bundle,从而支持热更新(hot reloading)。 首先,在项目根目录创建一个 metro.config.js 文件,内容如下: JavaScriptconst {getDefaultConfig} = require('@react-native/metro-config');
module.exports = getDefaultConfig(__dirname);
你可以参考 Community 模板中的 metro.config.js 文件。 配置文件就绪后,就可以启动 bundler 了。在项目根目录运行以下命令: npmYarnnpm start
yarn start
然后像平常一样构建并运行你的 Android 应用。 当你在应用中进入由 React 驱动的 Activity 时,它应该会从开发服务器加载 JavaScript 代码并显示: 在 Android Studio 中创建 release 构建 你也可以用 Android Studio 来创建 release 构建!就像为你原有的原生 Android 应用创建 release 构建一样简单。 React Native Gradle Plugin 会自动把 JS 代码打包进你的 APK/App Bundle 中。 如果你没有使用 Android Studio,也可以用以下命令创建 release 构建: cd android
# 构建 Release APK
./gradlew :app:assembleRelease
# 构建 Release AAB
./gradlew :app:bundleRelease
接下来该做什么? 到这里,你就可以像往常一样继续开发应用了。想深入了解 React Native 的开发,可以查阅我们的调试和部署文档。 核心概念 将 React Native 组件集成到 iOS 应用中的关键步骤是: 搭建正确的目录结构。 安装必要的 NPM 依赖。 在 Podfile 中配置 React Native。 用 TypeScript 编写你的第一个 React Native 页面。 通过 RCTRootView 将 React Native 与 iOS 代码集成。 运行 bundler 并实际运行应用,验证集成效果。 使用社区模板 阅读本指南时,建议对照 React Native Community Template。该模板包含一个最小化的 iOS 应用,能帮你理解如何将 React Native 集成到现有 iOS 应用中。 前置条件 先按照「搭建开发环境」和「不使用框架使用 React Native」的指南,配置好 iOS React Native 应用的开发环境。 本指南还假定你了解 iOS 开发的基础知识,比如创建 UIViewController 和编辑 Podfile。 1. 搭建目录结构 为了让后续流程顺畅,先为集成项目新建一个文件夹,然后把现有 iOS 项目移到 /ios 子文件夹中。 2. 安装 NPM 依赖 进入根目录,执行以下命令: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/package.json
这会把社区模板中的 package.json 文件复制到你的项目里。 接着安装 NPM 包: npmYarnnpm install
yarn install
安装完成后会生成一个新的 node_modules 文件夹,里面存放着构建项目所需的全部 JavaScript 依赖。 请把 node_modules/ 加入 .gitignore 文件(可参考社区的默认版本)。 3. 安装开发工具 Xcode Command Line Tools 安装 Command Line Tools:在 Xcode 菜单中选择 Settings...(或 Preferences...),进入 Locations 面板,在 Command Line Tools 下拉框中选择最新版本即可安装。 CocoaPods CocoaPods 是 iOS 和 macOS 开发的包管理工具,我们用它把 React Native 框架代码添加到当前项目中。 推荐通过 Homebrew 安装 CocoaPods: brew install cocoapods
4. 将 React Native 添加到你的应用中 配置 CocoaPods 要配置 CocoaPods,需要两个文件: 一个 Gemfile,用于定义需要哪些 Ruby 依赖。 一个 Podfile,用于定义如何正确安装这些依赖。 要创建 Gemfile,请进入项目根目录并运行以下命令: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/Gemfile
这会从模板中下载 Gemfile。 类似地,要创建 Podfile,请进入项目的 ios 文件夹并运行: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/ios/Podfile
请以 Community Template 作为 Gemfile 和 Podfile 的参考。 注意:记得修改 Podfile 中的这一行和这一行,使其与你的应用名称一致。如果你的应用没有测试,记得删除这个代码块。 接下来,我们需要运行几个额外的命令来安装 Ruby gems 和 Pods。 进入 ios 文件夹并运行以下命令: bundle install
bundle exec pod install
第一条命令安装 Ruby 依赖,第二条命令会把 React Native 代码集成到你的应用中,让 iOS 文件能够导入 React Native 的头文件。 5. 编写 TypeScript 代码 现在我们正式修改原生 iOS 应用,以集成 React Native。 我们要写的第一段代码,是即将集成到应用中的新界面的 React Native 代码。 创建 index.js 文件 首先,在 React Native 项目的根目录下创建一个空的 index.js 文件。 index.js 是 React Native 应用的入口文件,必不可少。它既可以是一个导入其他组件或应用代码的小文件,也可以直接包含所需的全部代码。 我们的 index.js 应如下所示(以 Community 模板文件为参考): JavaScriptimport {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);
创建 App.tsx 文件 接下来创建 App.tsx 文件。这是一个支持 JSX 表达式的 TypeScript 文件,包含我们要集成到 iOS 应用中的根 React Native 组件(链接): React TSXimport React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
style={{
backgroundColor: isDarkMode
? Colors.black
: Colors.white,
padding: 24,
}}>
第一步
编辑App.tsx 来
修改这个页面并查看效果。
查看你的改动
调试
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: '600',
},
bold: {
fontWeight: '700',
},
});
export default App;
这里有一份 Community 模板文件可供参考 5. 与现有 iOS 代码集成 接下来我们需要添加一些原生代码,用来启动 React Native 运行时,并让它渲染我们的 React 组件。 前提条件 React Native 需要配合 AppDelegate 工作。下面假设你的 AppDelegate 如下所示: ObjectiveCSwiftAppDelegate.m#import "AppDelegate.h"
#import "ViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
@end
AppDelegate.swiftimport UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 应用启动后的自定义配置入口。
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
更新 AppDelegate 类 首先,我们需要让 AppDelegate 继承 React Native 提供的类之一:RCTAppDelegate。 ObjectiveCSwift为此,我们需要修改 AppDelegate.h 和 AppDelegate.m 文件: 打开 AppDelegate.h 文件,按如下方式修改(可参考官方模板中的 AppDelegate.h): AppDelegate.h 的修改#import
+#import
-@interface AppDelegate : UIResponder
+@interface AppDelegate : RCTAppDelegate
@end
打开 AppDelegate.mm 文件,按如下方式修改(可参考官方模板中的 AppDelegate.mm): AppDelegate.mm#import "AppDelegate.h"
#import "ViewController.h"
+#import
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.automaticallyLoadReactNativeWindow = NO;
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
+{
+ return [self bundleURL];
+}
+- (NSURL *)bundleURL
+{
+#if DEBUG
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
+#else
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
+#endif
+}
@end
我们来看看上面的代码: 我们继承了 RCTAppDelegate,并调用它的 application:didFinishLaunchingWithOptions 方法,这样所有 React Native 的初始化流程都交由基类处理。 我们把 automaticallyLoadReactNativeWindow 设为 NO 来定制 RCTAppDelegate,告诉 React Native 应用会自己管理 UIWindow,它无需操心。 sourceURLForBridge: 和 bundleURL 方法用于告诉 React Native 应该到哪里找到需要渲染的 JS bundle。其中 sourceURLForBridge: 是旧架构(Old Architecture)的方法,可以看到它把具体决定交给了新架构(New Architecture)所需的 bundleURL 方法。 要实现这些,我们需要修改 AppDelegate.swift。 打开 AppDelegate.swift,按下述方式修改(可参考官方模板中的 AppDelegate.swift): AppDelegate.swiftimport UIKit
+import React_RCTAppDelegate
@main
-class AppDelegate: UIResponder, UIApplicationDelegate {
+class AppDelegate: RCTAppDelegate {
- var window: UIWindow?
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
+ self.automaticallyLoadReactNativeWindow = false
+ super.application(application, didFinishLaunchingWithOptions: launchOptions)
window = UIWindow()
- window?.rootViewController = ViewController()
- window?.makeKeyAndVisible()
+ window.rootViewController = ViewController()
+ window.makeKeyAndVisible()
return true
}
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
+ self.bundleURL()
+ }
+ override func bundleURL() -> URL? {
+#if DEBUG
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
+#else
+ Bundle.main.url(forResource: "main", withExtension: "jsbundle")
+#endif
+ }
}
我们来看看上面的代码: 我们继承了 RCTAppDelegate,并调用了它的 application(_:didFinishLaunchingWithOptions:) 方法。这样就把所有 React Native 的初始化流程交给了基类处理。 我们通过把 automaticallyLoadReactNativeWindow 设为 false 来定制 RCTAppDelegate。这一步告诉 React Native:UIWindow 由应用自己管理,React Native 无需操心。 sourceURLForBridge(for:) 和 bundleURL() 这两个方法用于告诉 React Native 在哪里找到需要渲染的 JS bundle。其中 sourceURLForBridge(for:) 来自旧架构(Old Architecture),可以看到它把决定权交给了 bundleURL() 方法——这是新架构(New Architecture)所要求的。 在 rootViewController 中展示 React Native 视图 最后,我们可以展示 React Native 视图了。为此,我们需要一个新的 View Controller,它能承载一个用于加载 JS 内容的视图。 在 Xcode 中新建一个 UIViewController(命名为 ReactViewController)。 让 Initial ViewController 展示 ReactViewController。具体做法有多种,取决于你的应用结构。本例中,我们假设你有一个按钮,点击后以模态方式展示 React Native。 ObjectiveCSwiftViewController.m#import "ViewController.h"
+#import "ReactViewController.h"
@interface ViewController ()
@end
- @implementation ViewController
+@implementation ViewController {
+ ReactViewController *reactViewController;
+}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.systemBackgroundColor;
+ UIButton *button = [UIButton new];
+ [button setTitle:@"Open React Native" forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.systemBlueColor forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.blueColor forState:UIControlStateHighlighted];
+ [button addTarget:self action:@selector(presentReactNative) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:button];
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ [NSLayoutConstraint activateConstraints:@[
+ [button.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+ [button.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
+ [button.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
+ [button.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ ]];
}
+- (void)presentReactNative
+{
+ if (reactViewController == NULL) {
+ reactViewController = [ReactViewController new];
+ }
+ [self presentViewController:reactViewController animated:YES completion:nil];
+}
@end
ViewController.swiftimport UIKit
class ViewController: UIViewController {
+ var reactViewController: ReactViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = .systemBackground
+ let button = UIButton()
+ button.setTitle("Open React Native", for: .normal)
+ button.setTitleColor(.systemBlue, for: .normal)
+ button.setTitleColor(.blue, for: .highlighted)
+ button.addAction(UIAction { [weak self] _ in
+ guard let self else { return }
+ if reactViewController == nil {
+ reactViewController = ReactViewController()
+ }
+ present(reactViewController!, animated: true)
+ }, for: .touchUpInside)
+ self.view.addSubview(button)
+
+ button.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
+ button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
+ button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
+ button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
+ ])
}
}
接下来按如下方式更新 ReactViewController 的代码: ObjectiveCSwiftReactViewController.m#import "ReactViewController.h"
+#import
+#import
@interface ReactViewController ()
@end
@implementation ReactViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
+ RCTRootViewFactory *factory = ((RCTAppDelegate *)RCTSharedApplication().delegate).rootViewFactory;
+ self.view = [factory viewWithModuleName:@"HelloWorld"];
}
@end
ReactViewController.swiftimport UIKit
+import React_RCTAppDelegate
class ReactViewController: UIViewController {
```objc override func viewDidLoad() {
super.viewDidLoad()
+ let factory = (RCTSharedApplication()?.delegate as? RCTAppDelegate)?.rootViewFactory
+ self.view = factory?.view(withModuleName: "HelloWorld")
}
}
``` 请务必禁用 Sandbox 脚本。具体做法:在 Xcode 中点击你的 app,进入 Build Settings,搜索 script,把 User Script Sandboxing 设为 NO。这一步是为了能在 React Native 自带的 Hermes 引擎的 Debug 和 Release 版本之间正常切换。 ;6. 测试集成效果 集成 React Native 到应用的基础步骤已经全部完成。接下来我们会启动 Metro bundler,把你的 TypeScript 应用代码打包成 bundle。Metro 的 HTTP 服务器会在你的开发环境中通过 localhost 把 bundle 提供给模拟器或设备,从而支持热重载。 首先,你需要在项目根目录创建一个 metro.config.js 文件,内容如下: JavaScript const {getDefaultConfig} = require('@react-native/metro-config');
module.exports = getDefaultConfig(__dirname);
你可以参考 Community 模板里的 metro.config.js 文件。 配置文件准备好之后,就可以启动 bundler 了。在项目根目录运行以下命令: npmYarn npm start
yarn start
然后像平常一样构建并运行你的 iOS 应用。 当你在应用中打开由 React 驱动的页面时,它应该会从开发服务器加载 JavaScript 代码并显示: 在 Xcode 中创建 release 构建 用 Xcode 同样可以创建 release 构建!唯一需要额外做的一步,是添加一个在构建时执行的脚本,把你的 JS 和图片打包进 iOS 应用中。 在 Xcode 中选中你的应用 点击 Build Phases 点击左上角的 +,选择 New Run Script Phase 点击 Run Script 这一行,把脚本重命名为 Bundle React Native code and images 把以下脚本粘贴到文本框中 Build React Native code and image set -e
WITH_ENVIRONMENT="$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
REACT_NATIVE_XCODE="$REACT_NATIVE_PATH/scripts/react-native-xcode.sh"
/bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE"
把这个脚本拖到名为 [CP] Embed Pods Frameworks 的脚本之前。 现在以 Release 模式构建应用,就能正常工作了。 接下来做什么? 到这里,你就可以像平常一样继续开发应用了。想深入了解 React Native 的使用,请参考我们的调试和部署文档。
这会把 Community template 中的 package.json 文件复制到你的项目里。 接着运行以下命令安装 NPM 依赖包: npmYarnnpm install
yarn install
安装过程会创建一个新的 node_modules 文件夹,用于存放构建项目所需的全部 JavaScript 依赖。 请将 node_modules/ 添加到 .gitignore 文件中(可参考社区的默认版本)。 3. 将 React Native 集成到应用中 配置 Gradle React Native 通过 React Native Gradle Plugin 来配置依赖和项目设置。 首先编辑 settings.gradle 文件,加入以下几行(参照社区模板的建议): Groovy// 配置用于自动链接的 React Native Gradle Settings 插件
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
// 如果使用 .gradle.kts 文件:
// extensions.configure
includeBuild("../node_modules/@react-native/gradle-plugin")
// 在这里包含你现有的 Gradle 模块。
// include(":app")
然后打开顶层的 build.gradle,加入这一行(参照社区模板的建议): Diffbuildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
+ classpath("com.facebook.react:react-native-gradle-plugin")
}
}
这样可以确保 React Native Gradle Plugin(RNGP)在项目中可用。 最后,在应用的 build.gradle 文件中加入以下几行(注意这是另一个 build.gradle 文件,通常位于 app 文件夹内——可参考社区模板中的对应文件): Diffapply plugin: "com.android.application"
+apply plugin: "com.facebook.react"
repositories {
mavenCentral()
}
dependencies {
// 其他依赖放在这里
+ // 注意:这里故意不指定版本号,由 RNGP 自动处理。
+ // 如果不使用 RNGP,则需要手动指定版本。
+ implementation("com.facebook.react:react-android")
+ implementation("com.facebook.react:hermes-android")
}
+react {
+ // 启用 Autolinking 所需配置 - https://github.com/react-native-community/cli/blob/master/docs/autolinking.md
+ autolinkLibrariesWithApp()
+}
最后,打开你的应用 gradle.properties 文件,添加以下几行(可参考 Community 模板文件): Diff+reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
+newArchEnabled=true
+hermesEnabled=true
配置 Manifest 文件 首先,确保 AndroidManifest.xml 中声明了网络权限: Diff
+
然后,在 debug 的 AndroidManifest.xml 中启用明文传输: Diff
+ tools:targetApi="28"
/>
老规矩,Community 模板里的 AndroidManifest.xml 文件可供参考:main 和 debug 这一步是必须的,因为应用需要通过 HTTP 与本地的打包工具 Metro 通信。 注意:这些配置只能加在 debug 的 manifest 里。 4. 编写 TypeScript 代码 接下来我们正式修改原生 Android 应用,集成 React Native。 第一步是编写新页面的 React Native 代码,之后会把它集成到我们的应用里。 创建 index.js 文件 首先,在 React Native 项目的根目录下创建一个空的 index.js 文件。 index.js 是 React Native 应用的入口,必不可少。它既可以是一个很小的文件,只负责导入 React Native 组件或应用的其他代码,也可以直接包含全部所需代码。 我们的 index.js 如下所示(可参考 Community 模板文件): JavaScriptimport {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);
创建 App.tsx 文件 接着创建一个 App.tsx 文件。这是一个支持 JSX 语法的 TypeScript 文件,包含我们要集成到 Android 应用中的 React Native 根组件(链接): React TSXimport React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Here the Community template file as reference 5. 与现有 Android 代码集成 接下来我们需要添加一些原生代码,用来启动 React Native 运行时,并让它渲染我们的 React 组件。 更新你的 Application 类 首先,需要更新 Application 类,以正确初始化 React Native,如下所示: JavaKotlinDiffpackage
import android.app.Application;
+import com.facebook.react.PackageList;
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactHost;
+import com.facebook.react.ReactNativeHost;
+import com.facebook.react.ReactPackage;
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
+import com.facebook.react.defaults.DefaultReactHost;
+import com.facebook.react.defaults.DefaultReactNativeHost;
+import com.facebook.soloader.SoLoader;
+import com.facebook.react.soloader.OpenSourceMergedSoMapping
+import java.util.List;
-class MainApplication extends Application {
+class MainApplication extends Application implements ReactApplication {
+ @Override
+ public ReactNativeHost getReactNativeHost() {
+ return new DefaultReactNativeHost(this) {
+ @Override
+ protected List
+ @Override
+ protected String getJSMainModuleName() { return "index"; }
+ @Override
+ public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; }
+ @Override
+ protected boolean isNewArchEnabled() { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; }
+ @Override
+ protected Boolean isHermesEnabled() { return BuildConfig.IS_HERMES_ENABLED; }
+ };
+ }
+ @Override
+ public ReactHost getReactHost() {
+ return DefaultReactHost.getDefaultReactHost(getApplicationContext(), getReactNativeHost());
+ }
@Override
public void onCreate() {
super.onCreate();
+ SoLoader.init(this, OpenSourceMergedSoMapping);
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ DefaultNewArchitectureEntryPoint.load();
+ }
}
}
Diff// package
import android.app.Application
+import com.facebook.react.PackageList
+import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
+import com.facebook.react.ReactNativeHost
+import com.facebook.react.ReactPackage
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
+import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
+import com.facebook.react.defaults.DefaultReactNativeHost
+import com.facebook.soloader.SoLoader
+import com.facebook.react.soloader.OpenSourceMergedSoMapping
-class MainApplication : Application() {
+class MainApplication : Application(), ReactApplication {
+ override val reactNativeHost: ReactNativeHost =
+ object : DefaultReactNativeHost(this) {
+ override fun getPackages(): List
+ override fun getJSMainModuleName(): String = "index"
+ override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
+ override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+ override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
+ }
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
+ SoLoader.init(this, OpenSourceMergedSoMapping)
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ load()
+ }
}
}
照例,这里提供 Community 模板中的 MainApplication.kt 文件作为参考 创建 ReactActivity 最后,我们需要创建一个新的 Activity,让它继承 ReactActivity 并承载 React Native 代码。这个 Activity 负责启动 React Native 运行时并渲染 React 组件。 JavaKotlinJava// package
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
public class MyReactActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "HelloWorld";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new DefaultReactActivityDelegate(this, getMainComponentName(), DefaultNewArchitectureEntryPoint.getFabricEnabled());
}
}
Kotlin// package
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
class MyReactActivity : ReactActivity() {
override fun getMainComponentName(): String = "HelloWorld"
override fun createReactActivityDelegate(): ReactActivityDelegate =
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}
照例,这里提供 Community 模板中的 MainActivity.kt 文件作为参考 每次创建新的 Activity,都需要把它注册到 AndroidManifest.xml 文件中。另外,还要把 MyReactActivity 的主题设置为 Theme.AppCompat.Light.NoActionBar(或其他不带 ActionBar 的主题),否则应用会在 React Native 界面上方显示一个 ActionBar: Diff
+
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+
现在你的 Activity 已经可以运行 JavaScript 代码了。 6. 测试集成效果 到这里,集成 React Native 的所有基础步骤都已完成。接下来我们启动 Metro bundler,把 TypeScript 应用代码打包成 bundle。Metro 的 HTTP 服务器会在你的开发环境中通过 localhost 向模拟器或设备提供 bundle,从而支持热更新(hot reloading)。 首先,在项目根目录创建一个 metro.config.js 文件,内容如下: JavaScriptconst {getDefaultConfig} = require('@react-native/metro-config');
module.exports = getDefaultConfig(__dirname);
你可以参考 Community 模板中的 metro.config.js 文件。 配置文件就绪后,就可以启动 bundler 了。在项目根目录运行以下命令: npmYarnnpm start
yarn start
然后像平常一样构建并运行你的 Android 应用。 当你在应用中进入由 React 驱动的 Activity 时,它应该会从开发服务器加载 JavaScript 代码并显示: 在 Android Studio 中创建 release 构建 你也可以用 Android Studio 来创建 release 构建!就像为你原有的原生 Android 应用创建 release 构建一样简单。 React Native Gradle Plugin 会自动把 JS 代码打包进你的 APK/App Bundle 中。 如果你没有使用 Android Studio,也可以用以下命令创建 release 构建: cd android
# 构建 Release APK
./gradlew :app:assembleRelease
# 构建 Release AAB
./gradlew :app:bundleRelease
接下来该做什么? 到这里,你就可以像往常一样继续开发应用了。想深入了解 React Native 的开发,可以查阅我们的调试和部署文档。 核心概念 将 React Native 组件集成到 iOS 应用中的关键步骤是: 搭建正确的目录结构。 安装必要的 NPM 依赖。 在 Podfile 中配置 React Native。 用 TypeScript 编写你的第一个 React Native 页面。 通过 RCTRootView 将 React Native 与 iOS 代码集成。 运行 bundler 并实际运行应用,验证集成效果。 使用社区模板 阅读本指南时,建议对照 React Native Community Template。该模板包含一个最小化的 iOS 应用,能帮你理解如何将 React Native 集成到现有 iOS 应用中。 前置条件 先按照「搭建开发环境」和「不使用框架使用 React Native」的指南,配置好 iOS React Native 应用的开发环境。 本指南还假定你了解 iOS 开发的基础知识,比如创建 UIViewController 和编辑 Podfile。 1. 搭建目录结构 为了让后续流程顺畅,先为集成项目新建一个文件夹,然后把现有 iOS 项目移到 /ios 子文件夹中。 2. 安装 NPM 依赖 进入根目录,执行以下命令: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/package.json
这会把社区模板中的 package.json 文件复制到你的项目里。 接着安装 NPM 包: npmYarnnpm install
yarn install
安装完成后会生成一个新的 node_modules 文件夹,里面存放着构建项目所需的全部 JavaScript 依赖。 请把 node_modules/ 加入 .gitignore 文件(可参考社区的默认版本)。 3. 安装开发工具 Xcode Command Line Tools 安装 Command Line Tools:在 Xcode 菜单中选择 Settings...(或 Preferences...),进入 Locations 面板,在 Command Line Tools 下拉框中选择最新版本即可安装。 CocoaPods CocoaPods 是 iOS 和 macOS 开发的包管理工具,我们用它把 React Native 框架代码添加到当前项目中。 推荐通过 Homebrew 安装 CocoaPods: brew install cocoapods
4. 将 React Native 添加到你的应用中 配置 CocoaPods 要配置 CocoaPods,需要两个文件: 一个 Gemfile,用于定义需要哪些 Ruby 依赖。 一个 Podfile,用于定义如何正确安装这些依赖。 要创建 Gemfile,请进入项目根目录并运行以下命令: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/Gemfile
这会从模板中下载 Gemfile。 类似地,要创建 Podfile,请进入项目的 ios 文件夹并运行: curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.77-stable/template/ios/Podfile
请以 Community Template 作为 Gemfile 和 Podfile 的参考。 注意:记得修改 Podfile 中的这一行和这一行,使其与你的应用名称一致。如果你的应用没有测试,记得删除这个代码块。 接下来,我们需要运行几个额外的命令来安装 Ruby gems 和 Pods。 进入 ios 文件夹并运行以下命令: bundle install
bundle exec pod install
第一条命令安装 Ruby 依赖,第二条命令会把 React Native 代码集成到你的应用中,让 iOS 文件能够导入 React Native 的头文件。 5. 编写 TypeScript 代码 现在我们正式修改原生 iOS 应用,以集成 React Native。 我们要写的第一段代码,是即将集成到应用中的新界面的 React Native 代码。 创建 index.js 文件 首先,在 React Native 项目的根目录下创建一个空的 index.js 文件。 index.js 是 React Native 应用的入口文件,必不可少。它既可以是一个导入其他组件或应用代码的小文件,也可以直接包含所需的全部代码。 我们的 index.js 应如下所示(以 Community 模板文件为参考): JavaScriptimport {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);
创建 App.tsx 文件 接下来创建 App.tsx 文件。这是一个支持 JSX 表达式的 TypeScript 文件,包含我们要集成到 iOS 应用中的根 React Native 组件(链接): React TSXimport React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
backgroundColor={backgroundStyle.backgroundColor}
/>
style={backgroundStyle}>
backgroundColor: isDarkMode
? Colors.black
: Colors.white,
padding: 24,
}}>
编辑
修改这个页面并查看效果。
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: '600',
},
bold: {
fontWeight: '700',
},
});
export default App;
这里有一份 Community 模板文件可供参考 5. 与现有 iOS 代码集成 接下来我们需要添加一些原生代码,用来启动 React Native 运行时,并让它渲染我们的 React 组件。 前提条件 React Native 需要配合 AppDelegate 工作。下面假设你的 AppDelegate 如下所示: ObjectiveCSwiftAppDelegate.m#import "AppDelegate.h"
#import "ViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
@end
AppDelegate.swiftimport UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 应用启动后的自定义配置入口。
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
更新 AppDelegate 类 首先,我们需要让 AppDelegate 继承 React Native 提供的类之一:RCTAppDelegate。 ObjectiveCSwift为此,我们需要修改 AppDelegate.h 和 AppDelegate.m 文件: 打开 AppDelegate.h 文件,按如下方式修改(可参考官方模板中的 AppDelegate.h): AppDelegate.h 的修改#import
+#import
-@interface AppDelegate : UIResponder
+@interface AppDelegate : RCTAppDelegate
@end
打开 AppDelegate.mm 文件,按如下方式修改(可参考官方模板中的 AppDelegate.mm): AppDelegate.mm#import "AppDelegate.h"
#import "ViewController.h"
+#import
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.automaticallyLoadReactNativeWindow = NO;
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
+{
+ return [self bundleURL];
+}
+- (NSURL *)bundleURL
+{
+#if DEBUG
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
+#else
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
+#endif
+}
@end
我们来看看上面的代码: 我们继承了 RCTAppDelegate,并调用它的 application:didFinishLaunchingWithOptions 方法,这样所有 React Native 的初始化流程都交由基类处理。 我们把 automaticallyLoadReactNativeWindow 设为 NO 来定制 RCTAppDelegate,告诉 React Native 应用会自己管理 UIWindow,它无需操心。 sourceURLForBridge: 和 bundleURL 方法用于告诉 React Native 应该到哪里找到需要渲染的 JS bundle。其中 sourceURLForBridge: 是旧架构(Old Architecture)的方法,可以看到它把具体决定交给了新架构(New Architecture)所需的 bundleURL 方法。 要实现这些,我们需要修改 AppDelegate.swift。 打开 AppDelegate.swift,按下述方式修改(可参考官方模板中的 AppDelegate.swift): AppDelegate.swiftimport UIKit
+import React_RCTAppDelegate
@main
-class AppDelegate: UIResponder, UIApplicationDelegate {
+class AppDelegate: RCTAppDelegate {
- var window: UIWindow?
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
+ self.automaticallyLoadReactNativeWindow = false
+ super.application(application, didFinishLaunchingWithOptions: launchOptions)
window = UIWindow()
- window?.rootViewController = ViewController()
- window?.makeKeyAndVisible()
+ window.rootViewController = ViewController()
+ window.makeKeyAndVisible()
return true
}
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
+ self.bundleURL()
+ }
+ override func bundleURL() -> URL? {
+#if DEBUG
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
+#else
+ Bundle.main.url(forResource: "main", withExtension: "jsbundle")
+#endif
+ }
}
我们来看看上面的代码: 我们继承了 RCTAppDelegate,并调用了它的 application(_:didFinishLaunchingWithOptions:) 方法。这样就把所有 React Native 的初始化流程交给了基类处理。 我们通过把 automaticallyLoadReactNativeWindow 设为 false 来定制 RCTAppDelegate。这一步告诉 React Native:UIWindow 由应用自己管理,React Native 无需操心。 sourceURLForBridge(for:) 和 bundleURL() 这两个方法用于告诉 React Native 在哪里找到需要渲染的 JS bundle。其中 sourceURLForBridge(for:) 来自旧架构(Old Architecture),可以看到它把决定权交给了 bundleURL() 方法——这是新架构(New Architecture)所要求的。 在 rootViewController 中展示 React Native 视图 最后,我们可以展示 React Native 视图了。为此,我们需要一个新的 View Controller,它能承载一个用于加载 JS 内容的视图。 在 Xcode 中新建一个 UIViewController(命名为 ReactViewController)。 让 Initial ViewController 展示 ReactViewController。具体做法有多种,取决于你的应用结构。本例中,我们假设你有一个按钮,点击后以模态方式展示 React Native。 ObjectiveCSwiftViewController.m#import "ViewController.h"
+#import "ReactViewController.h"
@interface ViewController ()
@end
- @implementation ViewController
+@implementation ViewController {
+ ReactViewController *reactViewController;
+}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.systemBackgroundColor;
+ UIButton *button = [UIButton new];
+ [button setTitle:@"Open React Native" forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.systemBlueColor forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.blueColor forState:UIControlStateHighlighted];
+ [button addTarget:self action:@selector(presentReactNative) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:button];
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ [NSLayoutConstraint activateConstraints:@[
+ [button.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+ [button.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
+ [button.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
+ [button.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ ]];
}
+- (void)presentReactNative
+{
+ if (reactViewController == NULL) {
+ reactViewController = [ReactViewController new];
+ }
+ [self presentViewController:reactViewController animated:YES completion:nil];
+}
@end
ViewController.swiftimport UIKit
class ViewController: UIViewController {
+ var reactViewController: ReactViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = .systemBackground
+ let button = UIButton()
+ button.setTitle("Open React Native", for: .normal)
+ button.setTitleColor(.systemBlue, for: .normal)
+ button.setTitleColor(.blue, for: .highlighted)
+ button.addAction(UIAction { [weak self] _ in
+ guard let self else { return }
+ if reactViewController == nil {
+ reactViewController = ReactViewController()
+ }
+ present(reactViewController!, animated: true)
+ }, for: .touchUpInside)
+ self.view.addSubview(button)
+
+ button.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
+ button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
+ button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
+ button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
+ ])
}
}
接下来按如下方式更新 ReactViewController 的代码: ObjectiveCSwiftReactViewController.m#import "ReactViewController.h"
+#import
+#import
@interface ReactViewController ()
@end
@implementation ReactViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
+ RCTRootViewFactory *factory = ((RCTAppDelegate *)RCTSharedApplication().delegate).rootViewFactory;
+ self.view = [factory viewWithModuleName:@"HelloWorld"];
}
@end
ReactViewController.swiftimport UIKit
+import React_RCTAppDelegate
class ReactViewController: UIViewController {
```objc override func viewDidLoad() {
super.viewDidLoad()
+ let factory = (RCTSharedApplication()?.delegate as? RCTAppDelegate)?.rootViewFactory
+ self.view = factory?.view(withModuleName: "HelloWorld")
}
}
``` 请务必禁用 Sandbox 脚本。具体做法:在 Xcode 中点击你的 app,进入 Build Settings,搜索 script,把 User Script Sandboxing 设为 NO。这一步是为了能在 React Native 自带的 Hermes 引擎的 Debug 和 Release 版本之间正常切换。 ;6. 测试集成效果 集成 React Native 到应用的基础步骤已经全部完成。接下来我们会启动 Metro bundler,把你的 TypeScript 应用代码打包成 bundle。Metro 的 HTTP 服务器会在你的开发环境中通过 localhost 把 bundle 提供给模拟器或设备,从而支持热重载。 首先,你需要在项目根目录创建一个 metro.config.js 文件,内容如下: JavaScript const {getDefaultConfig} = require('@react-native/metro-config');
module.exports = getDefaultConfig(__dirname);
你可以参考 Community 模板里的 metro.config.js 文件。 配置文件准备好之后,就可以启动 bundler 了。在项目根目录运行以下命令: npmYarn npm start
yarn start
然后像平常一样构建并运行你的 iOS 应用。 当你在应用中打开由 React 驱动的页面时,它应该会从开发服务器加载 JavaScript 代码并显示: 在 Xcode 中创建 release 构建 用 Xcode 同样可以创建 release 构建!唯一需要额外做的一步,是添加一个在构建时执行的脚本,把你的 JS 和图片打包进 iOS 应用中。 在 Xcode 中选中你的应用 点击 Build Phases 点击左上角的 +,选择 New Run Script Phase 点击 Run Script 这一行,把脚本重命名为 Bundle React Native code and images 把以下脚本粘贴到文本框中 Build React Native code and image set -e
WITH_ENVIRONMENT="$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
REACT_NATIVE_XCODE="$REACT_NATIVE_PATH/scripts/react-native-xcode.sh"
/bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE"
把这个脚本拖到名为 [CP] Embed Pods Frameworks 的脚本之前。 现在以 Release 模式构建应用,就能正常工作了。 接下来做什么? 到这里,你就可以像平常一样继续开发应用了。想深入了解 React Native 的使用,请参考我们的调试和部署文档。