如果你打算在新项目中使用当下最热门的ReactNative混合开发框架,可以参考ReactNative官网或者ReactNative中文网,其中有详细的教程讲解如何初始化一个ReactNative工程。这篇文章讲的是如何在已有项目中集成ReactNative(下文简称为RN)。
已有项目我们使用一个新建工程NativeAPP来代替。首先,我们使用Xcode创建一个新工程命名为NativeAPP。因为我是通过CocoaPods来添加RN链接库的,相信从事苹果应用开发的程序猿们都使用过CocoaPods这个简单方便的第三方库管理工具吧!这里就不赘述了,不熟悉的可以参照这篇文章CocoaPods的安装和使用。
安装React Native
1. 配置package.json安装依赖包
在NativeAPP的跟目录下创建一个package.json的配置文件,package.json我们也可以通过react-native init 创建一个新工程拷贝过来。
$ touch package.json
{
"name": "NativeAPP",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start",
},
"dependencies": {
"react": "15.3.2",
"react-native": "^0.35.0"
}
}
通过npm(Node Package Manager)安装React 和 React Native 到工程根目录的node_modules文件夹中:
$ nam install
2. 创建编辑RN入口index.ios.js文件
既然要集成RN,肯定需要加载RN的组件,安装成功后在NativeAPP的根目录下创建RN要加载的入口js文件index.ios.js:
$ touch index.ios.js
'use strict'
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
export default class NativeAPP extends Component {
render() {
return (
Welcome to React Native!
To get started, edit index.ios.js
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
);
}
}
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,
},
});
AppRegistry.registerComponent('NativeAPP', () => NativeAPP);
原生应用
1. 使用Cocoapods添加RN依赖库
使用终端在NativeAPP工程中创建Podfile:
$ pod init
使用vim打开Podfile编辑,添加RN所需要的链接库:
platform :ios, “8.0”
target 'NativeAPP' do
# 取决于你的工程如何组织,你的node_modules文件夹可能会在别的地方。
# 请将:path后面的内容修改为正确的路径(一定要确保正确~~)。
pod 'React', :path => ‘../node_modules/react-native', :subspecs => [
'Core',
'ART',
'RCTActionSheet',
'RCTAdSupport',
'RCTGeolocation',
'RCTImage',
'RCTNetwork',
'RCTPushNotification',
'RCTSettings',
'RCTText',
'RCTVibration',
'RCTWebSocket',
'RCTLinkingIOS',
]
end
可以根据需求添加工程中依赖的模块,例子中是都添加有,多添加是不会出现问题的,但是少添加是一定会出现问题的,会报“Native module cannot be null”的错误。
注意需要等到npm install安装完成后使用 pod install 来安装链接库,从它的加载路径我们可以看到是从本地进行加载的,这个时候我们本地如果还没有完成对应的Package的安装会报错。所以我们需要安装完成后再在再执行:
$ pod install
2. 配置HTTP请求白名单
在iOS 9以上的系统中,需要配置白名单,否则应用无法通过http协议连接到localhost主机。 如果不这样做,在尝试通过http连接到服务器时,会遭遇这个错误 - Could not connect to development server.
打开工程中的 Info.list 文件,添加下面配置即可:
NSAppTransportSecurity
NSExceptionDomains
localhost
NSTemporaryExceptionAllowsInsecureHTTPLoads
3. Native代码跳转RN
打开main.storyboard添加一个导航控制器,设置它的根视图控制器为ViewController,在根视图控制器的View上添加一个Button,并且绑定事件到VC中。
原生跳转到RN本质上是通过RCTRootView来加载jsbundle文件的,因此需要在ViewController导入RCTRootView的头文件和创建RCTRootView对象。
#import "RCTRootView.h"
按钮点击事件代码:
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
RCTRootView *rootView = [[RCTRootView alloc]
initWithBundleURL : jsCodeLocation
moduleName : @"NativeAPP"
initialProperties: nil
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self.navigationController pushViewController:vc animated:YES];
到这里在iOS原生应用中嵌入RN的工作已经完成了,接下来就是运行查看效果的时候了,在运行工程之前我们需要先将service服务运行起来,在项目的根目录下:
$ npm start
然后再使用Xcode运行我们的原生工程,运行效果图如下:
原生如何跳转到RN不同模块
原生嵌入RN一般会遇到有多个跳转RN的地方,而且跳转到不同模块中去,细心的同学是不是已经发现RCTRootView的:
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions;
方法中的字典类型** initialProperties**参数,没错我就是通过它来传递参数到js的props属性链上,然后在js根据参数来进行相应模块加载的(如果有其他更好的方法欢迎留言交流)。
按照同样的方法在ViewController再添加一个按钮,按钮的点击事件也一样,区别是在初期化RCTRootView时:
按钮一:
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"NativeAPP"
initialProperties : @{
@"launchOptions":@{
@"componentName":@"PageOne"
}
}
launchOptions : nil];
按钮二:
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"NativeAPP"
initialProperties : @{
@"launchOptions":@{
@"componentName":@"PageTwo"
}
}
launchOptions : nil];
接下来修改RN入口文件index.ios.js的渲染逻辑:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import CompontNameView from './modules/test'
class PageOne extends Component {
render() {
return(
这是页面一!
);
}
}
class PageTwo extends Component {
render() {
return(
这是页面二!
);
}
}
class NativeAPP extends Component {
render() {
var { launchOptions } = this.props;
if (launchOptions && launchOptions.componentName) {
switch (launchOptions.componentName) {
case 'PageOne':
return(
);
break;
case 'PageTwo':
return(
);
break;
default:
}
}
return (
Welcome to React Native!
To get started, edit index.ios.js
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
);
}
}
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,
},
});
AppRegistry.registerComponent('NativeAPP', () => NativeAPP);
因为只是展示效果,所以页面都很简单,下面是展示效果: