美文网首页
ReactNative入门篇

ReactNative入门篇

作者: 简书测试账号 | 来源:发表于2018-06-12 11:37 被阅读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

    相关文章

      网友评论

          本文标题:ReactNative入门篇

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