ReactNative入门篇

2018-06-12  本文已影响17人  简书测试账号

标题已经写得很清楚了,就是入门篇。所以要讲的就是以下几点:

1.环境搭建
2.RN demo创建及项目包结构简要分析
3.RN与Android交互(不要问为什么是跟Android交互,因为我就会Android开发)

由于本人是在mac上进行开发的,所以在环境搭建等其他问题上都是基于mac上进行的。废话不多说,直接进入主题内容。

环境搭建

首先先安装homebrew

当然不装也行,只是在安装node.js需要去官网下载安装包进行安装。还有就是homebrew还可以安装其他的软件,如python,所以如果没有装homebrew的建议安装。
打开终端,输入已经以下命令,即开始安装homebrew。

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

如果提示出错了Error: /usr/local/xxxxx is not writable. You should change the
ownership and permissions of /usr/local/xxxxx back to your
user account:
输入下面的命令行

sudo chown -R $(whoami) /usr/local/xxxxx

完成之后在输入上面的安装命令,到此就完成了homebrew安装,接下来就是正式的RN环境搭建了。

Node安装

上面花了些时间安装homebrew就是为了此刻使用homebrew来安装node。安装命令如下:

brew install node

安装完成后,我们需要做下npm镜像,原因是我们这的上网环境比较特殊。

npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https://npm.taobao.org/dist --global
Yarn、React Native的命令行工具安装

Yarn是Facebook提供的替代npm的工具,可以加速node模块的下载。yarn安装命令如下:

npm install -g yarn react-native-cli

安装完成后,我们同样需要做下yarn镜像。

yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global

到此我们算是完成RN环境的搭建。至于Android和IOS开发环境的搭建,就不在此进行叙述了,有需要的同学自行google或者百度。

node.js IDE推荐

虽然RN环境安装好了,但是使用什么IDE进行RN开发呢?我这边推荐两个比较大众的IDE,分别是webstorm和VS Code,其中webstorm需要收费。

由于本人使用的是VS Code,所以接下来就介绍下使用VS Code做RN开发需要用到的一些插件。(VS Code下载地址)

  1. React Native Tools:微软官方出的ReactNative插件
  2. Reactjs code snippets:react的代码提示,如componentWillMount方法可以通过cwm直接获得
    3.Auto Close Tag:自动闭合标签
    4.Auto Rename Tag:自动重命名标签
    5.Path Intellisense:文件路径提示补全

RN demo创建及项目包结构简要分析

RN demo创建

首先我们先创建一个名叫rn_demo的项目,具体项目创建方法如下:

react-native init rn_demo

完成之后,会提示我们怎么运行这个项目。如下图,就是我创建完成后的提示。


image.png

接着按提示操作,我这边是Android,所以我的操作如下:

cd rn_demo
react-native run-android

等待命名执行完成后,可以在安卓模拟器上看到如下的界面


F040062B-2815-4A9D-8619-FAFB2E29CA06.png
RN 项目结构简要分析

如下图,就是刚刚我们创建的项目的基本结构。


image.png

首先先看android和ios这两个文件夹,从命名上不难看出他们分别是android和ios源代码,暂时没什么好说,等后面的"RN与Android交互"会进一步说明。

接下来看node_modules这个文件夹,这是一个RN各种库的集中营,我们开发过程中引入的第三方库都会从远程中央仓库获取到本地,并存在这个目录下,因此该目录就会占用很大空间。为了节省时间,在使用svn或者git管理RN项目的时候,这个目录会被作为忽略项。因此当从svn或者git上拉取下来的项目是没有node_modules这个文件夹的。那么我们就需要通过以下的命令拉取项目中配置的库(新增第三方库同样也要执行如下命令)

npm install

或者

yarn install

package.json是项目的基本配置文件,文件里面各字段的大致解释入下图

image.png

app.json这个文件对于项目来说没什么用,文件内容如下图


image.png

app.js这个文件就是我们demo展示出来的页面了,具体内容如下

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} 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',
});

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}>
          To get started, edit App.js
        </Text>
        <Text style={styles.instructions}>
          {instructions}
        </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,
  },
});

最后来看index.js这个文件,内容就三行且每行都不可缺少的。具体如下图:


image.png

对应的Android中的代码如下图:


image.png

RN与Android交互

RN与Android交互有以下4种方式

直接常量传递(由RN发起,从安卓原生获取常量数据)
RN向Android传递数据
callBack和promise回调
RCTDeviceEventEmitter事件监听

在开始介绍这四种交互方式之前,我们需要先Android代码上创建RN与Android交互的桥接代码。

首先创建ExampleReactModule类,代码如下:

public class ExampleReactModule extends ReactContextBaseJavaModule {

    public ExampleReactModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "ExampleReactModule";
    }
}

从代码中我们看到了ExampleReactModule类继承了ReactContextBaseJavaModule和重写了getName,其中getName返回的字符串,将在RN中作为一个key,RN通过这个key来进行与原生进行交互。因此如果一个项目有多个ReactModule的时候,这个getName返回的字符串不能重复。

接着创建ExampleReactPackage类,代码如下:

public class ExampleReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> nativeModules = new ArrayList<>();
        nativeModules.add(new ExampleReactModule(reactContext));
        return nativeModules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

我们创建的ExampleReactPackage类,实现了ReactPackage接口,并且重写了createNativeModules与createViewManagers方法。其中createNativeModules方法返回了刚刚我们创建的ExampleReactModule类。再来看createViewManagers方法,虽然我们不需要返回我们的ViewManager,但是还是要返回一个空的数组,不然RN会报错误,无法正常运行。

最后将ExampleReactPackage配置到ReactNativeHost里面,代码如下:

public class MainApplication extends Application implements ReactApplication {


    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    new ExampleReactPackage()
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }

    public ReactContext getReactContext() {
        return mReactNativeHost.getReactInstanceManager().getCurrentReactContext();
    }
}

可以看到在getPackages方法里面我们实现了ExampleReactPackage对象。再来看getJSMainModuleName方法,返回的的字符串是不是很熟悉?没错返回的这个字符串就是对应RN的index.js,具体为什么是这样返回在这里不进行深入解析。接下来就正式开始介绍RN与Android交互的4种方法。

直接常量传递

直接常量传递,是RN主动发起,从Android获取提前设置好的数据。代码如下:
Android代码:

 @Nullable
    @Override
    public Map<String, Object> getConstants() {
        Map<String, Object> constants = new HashMap<>();
        constants.put("test", "安卓原生常量");
        return constants;
    }

RN调用代码如下:

getFinalValue=()=>{
    console.log(`安卓静态常量:${NativeModules.ExampleReactModule.test}`);
  }

从RN的调用代码中,可以发现是先找到Android代码返回的ExampleReactModule名称,接着找到getConstants()方法中的Map对应的Key的方式来获取到Android提供的常量。

RN向Android传递数据

标题已经明确的说出意图了,需要注意的是Android方法需要写上@ReactMethod注解,直接看代码:
Android代码如下:

 @ReactMethod
    public void handleMessage(String msg) {
        Toast.makeText(getReactApplicationContext(), "来至RN数据:" + msg, Toast.LENGTH_LONG).show();
        Log.e("RNMessage", "从RN传递过来的内容:" + msg);
    }

RN调用代码如下:

rnToAndroidClick = () => {
    NativeModules.ExampleReactModule.handleMessage('我来至RN');
  }
callBack和promise回调

callBack和promise两种方式的回调都是异步的,先看看这种两种方法都是怎么实现,最后再来看看是不是真的是异步回调。

先来看callBack的实现,代码如下

Android代码:

    @ReactMethod
    public void handleCallback(String msg, Callback callback) {
        Toast.makeText(getReactApplicationContext(), "安卓回调数据给RN", Toast.LENGTH_LONG).show();
        msg = msg + "-handleCallback-安卓回调";
        callback.invoke(msg);
    }

RN代码:

rnCallBack = () => {
    console.log(`rnCallBack start`);
    NativeModules.ExampleReactModule.handleCallback('我来至RN', (msg) => {
      console.log(`安卓回调的数据:${msg}`);
    });
    console.log(`rnCallBack end`);
  }

接着来看promise的代码实现

Android代码:

 @ReactMethod
    public void handlePromise(String msg, Promise promise) {
        try {
            Toast.makeText(getReactApplicationContext(), "handlePromise", Toast.LENGTH_LONG).show();
            msg = msg + "-handlePromise-安卓回调";
            promise.resolve(msg);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

RN代码:

  promiseToRn = () => {
    console.log(`promiseToRn start`);
    NativeModules.ExampleReactModule.handlePromise('使用Promise').then((msg) => {
      console.log(msg)
    }).catch((error) => {
      console.log(error)
    });
    console.log(`promiseToRn end`);
  }

可以看到在callback和promise的RN代码中都做了log打印,从promise的RN代码中可以看到代码顺序是"promiseToRn start"字样的log打印-handlePromise调用-"promiseToRn end"字样的log打印。如果是同步执行,那么日志打印肯定也是这种顺序,是不是真的这样呢?我们来看实际的代码执行结果,如下图:


image.png

从图中可以看出最后打印的是callback和promise两种回调,所以可以确定callback和promise是异步回调

RCTDeviceEventEmitter事件监听

这是一个观察者模式的实现,RN这边设置要监听的事件,Android负责事件的发出。具体实现代码如下:
Android代码:

 mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRn", "安卓主动传递数据给RN");

RN代码:

 DeviceEventEmitter.addListener('AndroidToRn', this.androidToRn.bind(this));
  androidToRn=(androidMsg)=>{
    console.log(`来至安卓的消息:${androidMsg}`);
  }

到此就结束了RN入门的分享,最后贴出完整的RN与Android交互的代码

ExampleReactModule.java

package com.rn_demo;

import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;


public class ExampleReactModule extends ReactContextBaseJavaModule {

    public ExampleReactModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "ExampleReactModule";
    }

    @Nullable
    @Override
    public Map<String, Object> getConstants() {
        Map<String, Object> constants = new HashMap<>();
        constants.put("test", "安卓原生常量");
        return constants;
    }

    @ReactMethod
    public void handleMessage(String msg) {
        Toast.makeText(getReactApplicationContext(), "来至RN数据:" + msg, Toast.LENGTH_LONG).show();
        Log.e("RNMessage", "从RN传递过来的内容:" + msg);
    }

    @ReactMethod
    public void handleCallback(String msg, Callback callback) {
        Toast.makeText(getReactApplicationContext(), "安卓回调数据给RN", Toast.LENGTH_LONG).show();
        msg = msg + "-handleCallback-安卓回调";
        callback.invoke(msg);
    }

    @ReactMethod
    public void handlePromise(String msg, Promise promise) {
        try {
            Toast.makeText(getReactApplicationContext(), "handlePromise", Toast.LENGTH_LONG).show();
            msg = msg + "-handlePromise-安卓回调";
            promise.resolve(msg);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void jumpToMyActivity() {
        Intent intent = new Intent(getCurrentActivity(), MyActivity.class);
        getCurrentActivity().startActivity(intent);
    }

}

MyActivity.java

package com.rn_demo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;

import com.facebook.react.ReactActivity;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;


public class MyActivity extends ReactActivity {

    private ReactContext mReactContext;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        //
        mReactContext = ((MainApplication) getApplication()).getReactContext();
    }

    public void sendToRn(View view) {
        mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRn", "安卓主动传递数据给RN");
    }
}

App.js

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

export default class App extends Component {

  componentWillMount(){
    DeviceEventEmitter.addListener('AndroidToRn', this.androidToRn.bind(this));
  }
  componentWillUnmount() {
    DeviceEventEmitter.removeAllListeners();
}

  render() {

    return (
      <View style={[styles.rootViewStyle]} flexDirection="column">
       <TouchableOpacity style={[styles.touchStyle]} onPress={this.getFinalValue}>
          <Text style={[styles.textButton]}>获取安卓常量</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.touchStyle]} onPress={this.rnToAndroidClick}>
          <Text style={[styles.textButton]}>从RN传数据给原生</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.touchStyle]} onPress={this.rnCallBack}>
          <Text style={[styles.textButton]}>使用callback返回数据给RN</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.touchStyle]} onPress={this.promiseToRn}>
          <Text style={[styles.textButton]}>Promise返回数据给RN</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.touchStyle]} onPress={this.jumpToMyActivity}>
          <Text style={[styles.textButton]}>跳转至MyActivity</Text>
        </TouchableOpacity>
      </View>
    );
  }

  getFinalValue=()=>{
    console.log(`安卓静态常量:${NativeModules.ExampleReactModule.test}`);
  }

  rnToAndroidClick = () => {
    NativeModules.ExampleReactModule.handleMessage('我来至RN');
  }

  rnCallBack = () => {
    console.log(`rnCallBack start`);
    NativeModules.ExampleReactModule.handleCallback('我来至RN', (msg) => {
      console.log(`安卓回调的数据:${msg}`);
    });
    console.log(`rnCallBack end`);
  }

  promiseToRn = () => {
    console.log(`promiseToRn start`);
    NativeModules.ExampleReactModule.handlePromise('使用Promise').then((msg) => {
      console.log(msg)
    }).catch((error) => {
      console.log(error)
    });
    console.log(`promiseToRn end`);
  }

  androidToRn=(androidMsg)=>{
    console.log(`来至安卓的消息:${androidMsg}`);
  }

  jumpToMyActivity=()=>{
    NativeModules.ExampleReactModule.jumpToMyActivity();
  }

}



const styles = StyleSheet.create({
  rootViewStyle: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  touchStyle: {
    width: 180,
    height: 45,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ff87a0',
    marginTop: 20
  },
  textButton: {
    fontSize: 16

  }

});

项目已经上传到github,有需要的可以到前往这进行下载rn_demo

上一篇下一篇

猜你喜欢

热点阅读