React Native集成到现有的原生项目

因为项目需求,在iOS原生项目中会嵌套几个RN界面,这就牵涉到了原生代码中嵌套RN代码的问题,至于集成步骤以及过程中遇到的坑都在下面一一列举,以帮助后来人。

前提

RN环境已经搭建完成,如若未完成,请移步
React Native环境搭建

将React Native集成到iOS应用中主要有如下几个步骤:

  1. 配置好React Native依赖和项目结构
  2. 了解你要集成的React Native组件
  3. 使用CocoaPods把这些组件以依赖的形式加入到项目中
  4. 创建js文件,编写React Native组件的js代码
  5. 在应用中添加一个RCTRootView,这个RCTRootView正是用来承载你的React Native组件的容器
  6. 启动React Native的Packager服务,运行应用
  7. 验证这部分组件是否正常工作

1. 配置项目目录结构

我在此也是为了更加深入的了解RN,所以在这里拿以前的小demo来做示范了。
首先创建一个空目录用于存放React Native项目,然后在这个空目录中创建一个ios子目录,把现有的iOS项目拷贝到ios子目录中

目录结构

2. 安装JavaScript依赖包

在项目根目录下创建一个名为package.json的文件,其中填入以下内容:

{
  "name": "MyReactNativeApp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  }
}

接下来使用yarn或者npm(两者都是node的包管理器)来安装React和React Native模块。
打开终端,进入到根目录中(即package.json)文件所在目录,然后运行以下命令安装:

yarn add react-native

这样默认安装最新版本的React Native,命令执行完毕后可以看到输出中有两个警告信息:

warning " > react-native@0.57.4" has unmet peer dependency "react@16.6.0-alpha.8af6728".
warning Your current version of Yarn is out of date. The latest version is "1.12.3", while you're on "1.9.4".

第一个警告提示还要安装指定版本的React,第二个警告说是yarn版本不是最新的
然后执行命令安装指定版本的React

yarn add react@16.6.0-alpha.8af6728

注意:必须严格匹配警告信息中所列出的版本,高了或者低了都是不可以嘀!

完成这个步骤之后可以发现我们的根目录下多了一些东西:

根目录

项目根目录下的node_modules目录中安装的都是JavaScript的以来模块(对于这个目录,我们的原则是不复制、不移动、不修改、不上传、随用随装)

node_modules/目录记录到.gitignore文件中(即不上传到版本控制系统,只保留到本地)

3. 安装CocoaPods

CocoaPods是针对iOS和Mac开发的包管理工具。执行一下命令安装CocoaPods

brew install cocoapods

安装就不用过多的说了(毕竟都安装的有)

4. 配置CocoaPods依赖

React Native框架整体是作为node模块安装到项目中的。接下来我们就要在CocoaPods的Podfile中指定我们需要使用的subspecs

可用的subspecs都列在node_modules/react-native/React.podspec文件中,基本上都是按照其功能命名的。一般来说第一个要添加的就是Core,其包含了必须的AppRegistryStyleSheetView以及其他的一些React Native核心库。如果要使用React Native的Text库(即<Text>组件),那就需要添加TCTTextsubspec,等等。

我们需要在Podfile文件中指定所需要的subspec。创建Podfile的最简单方法就是在/ios目录中使用CocoaPods的init命令(如果原先项目中已经创建了Podfile文件,可以跳过此步):

pod init

这样Podfile就已经创建了,接下来就需要调整其内容以满足集成需求,调整后的Podfile内容类似下面的:

# target的名字一般与你的项目名字相同
target 'NumberTileGame' do

  # 'node_modules'目录一般位于根目录中
  # 但是如果你的结构不同,那你就要根据实际路径修改下面的`:path`
  pod 'React', :path => '../node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge', # 如果RN版本 >= 0.47则加入此行
    'DevSupport', # 如果RN版本 >= 0.43,则需要加入此行才能开启开发者菜单
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # 调试功能需要此模块
    'RCTAnimation', # FlatList和原生动画功能需要此模块
    # 在这里继续添加你所需要的其他RN模块
  ]
  # 如果你的RN版本 >= 0.42.0,则加入下面这行
  pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'

  # 如果RN版本 >= 0.45则加入下面三个第三方编译依赖
  pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
  pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
  pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'

end

修改好Podfile文件后,就可以开始安装React Native的pod包了:

pod install

5. RN代码集成

依赖搞好之后,就可以愉快的搞代码咯,接下来我们在根目录中创建一个名为home.js的文件(个人建议功能文件单独创建文件夹放到一块,不要都挤在根目录中),实现以下内容(这里的内容可以随便咯):

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, NativeModules, NativeAppEventEmitter} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

var rn = NativeModules.HmRNViewController;

type Props = {};

export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <Text style={styles.instructions}>Hello World</Text>
        <Text style={styles.instructions}>这里是RN界面</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

代码码好了,接下来就是要iOS原生应用能够调用的到的这个页面,我们需要在项目根目录下创建一个空的index.js文件(入口文件建议放在根目录下)

index.js文件是React Native应用在iOS上的入口文件,该入口文件也可以像iOS的main入口文件一样简单,在其中实现以下内容即可完成:

import {AppRegistry} from 'react-native';
import home from './home.js';  // 引入当前目录下的home.js文件

// 注册组件
AppRegistry.registerComponent('homePage', () => home);

6. iOS代码集成

RN代码搞定,接下来就是在iOS原生项目中对接RN了,这也是重点:

假设当前的原生项目中A界面有一个按钮,点击之后要让跳转到RN界面,主要实现如下:
在iOS项目中新建一个ViewController界面B,在B界面的viewDidLoad方法中实现以下代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.title = @"B";
    NSURL *jsCodeLocation;
    jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
    RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"homePage" initialProperties:@{@"name" : @"Max", @"value" : @"123456"} launchOptions:nil];
    // 这里如果引入的是一个页面,可以使用self.view = rootView;,也可以使用[self.view addSubview:rootView];,不过使用addSubview:方法必须要给rootView设置frame
    //如果是一个组件(如按钮、轮播图等等),则可以直接使用[self.view addSubview:rootView];将组件添加到当前view,并设置frame值:rootView.frame = CGRectMake(0, 100, 100, 100);
    self.view = rootView;
}

A界面的按钮点击事件中实现的功能就是普通的跳转到B界面,这里就略去了

7. 运行项目

运行项目,点击A界面的按钮,即可跳转到RN界面:

报错咯

别慌别慌,要稳住,不就是一个红屏嘛 哈哈哈
出现这个红屏报错的原因在于:有没有发现运行项目少了点什么?一般情况下,RN项目运行的时候都会自动启动packager服务的,但是这里没有自动启动,至于怎么设置自动启动packager服务我暂时也不知道,但是可以手动启动的:

打开终端,到达项目根目录下(即package.json所在目录),然后执行命令:

npm start

然后终端形如:

启动packager服务

刚刚打开的终端就用于启动packager服务了,想要运行项目要么再打开一个终端至根目录下运行react-native run-ios,要么进入ios文件夹打开Xcode运行,运行结果如下:

运行结果

在RN界面如果不想要原生的导航栏,可以在B界面中隐藏导航栏,总之,随便搞起来吧

这篇文章所介绍的主要是更改目录结构,环境配置等等,具体代码部分很有限,所以源代码就不贴出来了。在后面还会陆续的贴出来我在学习RN中遇到的问题我和经验分享。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容