2.嫁接原生!

2016-10-29  本文已影响37人  炒鸡范

前言

昨天我们进行了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。
走到这一步,现在文件夹里是这样的

WechatIMG13.jpeg
是不是跟昨天的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可用.
感觉差不多了,运行下!

WechatIMG14.jpeg
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的方法

上一篇 下一篇

猜你喜欢

热点阅读