美文网首页
2.嫁接原生!

2.嫁接原生!

作者: 炒鸡范 | 来源:发表于2016-10-29 12:24 被阅读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的方法

    相关文章

      网友评论

          本文标题:2.嫁接原生!

          本文链接:https://www.haomeiwen.com/subject/ktteuttx.html