前言
昨天我们进行了RN初体验,大致了解了RN是怎么回事。结束的比较早,我就回去看了下JS的语法,大致明白了语法后就对昨天很多代码上的困惑有了新的理解。这里推荐大家看廖雪峰老师的JS教程,花几个小时入门下JS语法,对我们RN学习是必不可少的。
一、今日计划
今天依旧照着ReactNative中文网的教程来,昨天是入门,今天就是进阶指南,看能完成多少是多少,毕竟进阶了嘛没有昨天那么简单了。加油!
二、开始第二天的学习!
1、嵌入原生应用
这篇是没翻译还是咋的...反正我是看不懂。于是本吊网上搜索了一下,找到一篇能看的RN嵌入到现有iOS原生应用.
新建一个叫RNTest的iOS项目在桌面上。
打开项目文件夹,把RNTest、RNTest.xcodeproj放到新建的ios文件下。
新建一个文本文件package.json,输入:
{
"name": "RNTest",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "15.3.1",
"react-native": "0.36.0"
}
}
其中name改成RNTest,就是我们的ios项目名。
cd到这个目录下,执行以下命令npm install
,此步会根据上面的package.json文件安装node_modules。
走到这一步,现在文件夹里是这样的
是不是跟昨天的AwesomeProject差不多了!
然后我们进入ios文件夹下,
pod init
一下。然后编辑podfile
target 'RNTest' do
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTWebSocket', # needed for debugging
]
end
保存 然后pod install
这个大家都知道。
然后回到上级文件夹,也就是package.json所在的文件夹touch index.ios.js
,然后编辑index.ios.js
'use strict';
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class RNHighScores extends React.Component {
render() {
var contents = this.props["scores"].map(
score => <Text key={score.name}>{score.name}:{score.value}{"\n"}</Text>
);
return (
<View style={styles.container}>
<Text style={styles.highScoresTitle}>
2048 High Scores!
</Text>
<Text style={styles.scores}>
{contents}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
highScoresTitle: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
scores: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
// Module name
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
打开RNTest.xworkspace,buildsphase里添加libc++.std.
编辑viewcontroller.m
#import "ViewController.h"
#import "RCTRootView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)highScoreButtonPressed:(id)sender {
NSLog(@"High Score Button Pressed");
NSURL *jsCodeLocation = [NSURL
URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"RNHighScores"
initialProperties :
@{
@"scores" : @[
@{
@"name" : @"Alex",
@"value": @"42"
},
@{
@"name" : @"Joel",
@"value": @"10"
}
]
}
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self presentViewController:vc animated:YES completion:nil];
}
@end
main.storyboard随便拖一个按钮,然后把按钮的touchUpInside和highScoreButtonPressed链接起来。
然后设置iOS项目plist的http可用.
感觉差不多了,运行下!
shit...
说的是“无法连接到开发服务器,请确保:node服务器正在运行在同一个网络下,使用'npm start'从RN的根目录”。
恩应该就是这个意思。
命令行
npm start
,发现还是不行
WechatIMG15.jpeg
我查了半天,发现是在pod里面少了一个组件RCTNetwork
,
修改podfile
target 'RNTest' do
pod 'React', :path => 'node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # needed for debugging
]
end
pod install一下,然后运行
WechatIMG16.jpeg终于正常了LOL
然后让我们来研究一下OC部分的代码。
- (IBAction)highScoreButtonPressed:(id)sender {
NSLog(@"High Score Button Pressed");
//这里设置了主入口文件的请求路径
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
//RCTRootView是一个UIView
//传入请求路径、模块名称、初始属性(是一个字典)
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"RNHighScores"
initialProperties :
@{
@"scores" : @[
@{
@"name" : @"Alex",
@"value": @"42"
},
@{
@"name" : @"Joel",
@"value": @"10"
}
]
}
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
//然后创建一个控制器,将rootView赋给控制器的view,就能看到
vc.view = rootView;
[self presentViewController:vc animated:YES completion:nil];
}
然后看下index.ios.js的代码
class RNHighScores extends React.Component {
render() {
//这里将OC中传入的初始参数initialProperties取其中的["score"]做map操作
//map操作的意思是将列表中的对象依次执行括号中的函数,并返回给contents一个最终处理完的数组
//那么contents应该会是一个'<Text key='Alex'}>{Alex}:{42}{"\n"}</Text>,<Text key={Joel}>{Joel}:{10}{"\n"}</Text>'
var contents = this.props["scores"].map(
score => <Text key={score.name}>{score.name}:{score.value}{"\n"}</Text>
);
console.log('========================'+contents);
return (
<View style={styles.container}>
<Text style={styles.highScoresTitle}>
2048 High Scores!
</Text>
<Text style={styles.scores}>
//然后在这里返回了刚才处理完的数组contents
{contents}
</Text>
</View>
);
}
}
恩集成RN到iOS项目的初次尝试就完成了。
2.原生模块
没错我们跳过了很多,也不是跳过,只是先来看这个模块。
为什么,因为我们刚刚在iOS项目中接入了RN,就先来尝试下和原生的交互。
原生模块这个教程看过去有点晦涩难懂,我就先来做个demo尝试下。
在iOS项目中我们定义一个Person对象,继承自NSObject;
在Person.h中这么写
#import <Foundation/Foundation.h>
//引入RCTBridgeModule
#import "RCTBridgeModule.h"
//遵循RCTBridgeModule协议
@interface Person : NSObject<RCTBridgeModule>
@end
在Person.m中
#import "Person.h"
@interface Person ()
@property (nonatomic,copy) NSString *name;
@end
@implementation Person
//这里必须包含RCT_EXPORT_MODULE()宏,个宏也可以添加一个参数用来指定在Javascript中访问这个模块的名字。如果你不指定,默认就会使用这个Objective-C类的名字。
RCT_EXPORT_MODULE();
//然后我们定义了一个sayName方法,先不管是+还是-方法,RN的要求是返回值必须是void,可以带参数也可以不带
RCT_EXPORT_METHOD(sayName:(NSString *)name)
{
//然后输出下
NSLog(@"我是%@",name);
}
@end
然后修改index.io.js,这里我们删掉多余的代码,看上去清晰点
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,//这里需要引入NativeModules模块
} from 'react-native';
class RNHighScores extends React.Component {
render() {
//这里从NativeModules中获取Person,也就是RCT_EXPORT_MODULE()作用所在
//如果你将其改为RCT_EXPORT_MODULE(FFFFFF),这里就将调用NativeModules.FFFFFF
var Person = NativeModules.Person;
//然后调用Person的sayName方法
Person.sayName('饭饭');
return (
<View>
<Text> 1111 </Text>
</View>
);
}
}
// Module name
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
运行,发现sayName方法被调用,控制台输出我是饭饭
这是我们完成了JS调用原生代码的过程,那么我们如何向JS传值呢?
于是我们继续修改代码:
#import "Person.h"
@interface Person ()
@property (nonatomic,copy) NSString *name;
@end
@implementation Person
RCT_EXPORT_MODULE();
//看到这里,传递的参数变成了一个RCTResponseSenderBlock,
//查看它的定义可以知道它是一个void (^RCTResponseSenderBlock)(NSArray *response);传递一个NSArray。
RCT_EXPORT_METHOD(sayName:(RCTResponseSenderBlock)callback)
{
NSLog(@"native调用");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//这里我们就调用callback,传递一个数组,只有一个字符串元素
callback(@[@"js调用"]);
});
}
@end
然后修改index.ios.js
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,
} from 'react-native';
class RNHighScores extends React.Component {
render() {
var Person = NativeModules.Person;
//我们看到,sayName的传递参数发生了改变
//()=>{}是JS定义函数的一种方式,括号里的是形参,花括号里的是函数体
//也就是我们从sayName调用的callback实际对应的就是这里的()=>{}这个函数
Person.sayName((str) => {
console.log(str);
});
return (
<View>
<Text> 1111 </Text>
</View>
);
}
}
// Module name
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
运行一下,sayName被调用,同时控制台输出:
**2016-10-28 16:53:09.691 RNTest[67337:745163] native调用
**2016-10-28 16:53:11.985 [info][tid:com.facebook.react.JavaScript] js调用
总结:这里我们大致知道了js调用Native和native回调JS的方法
网友评论