ReactNative入门篇
标题已经写得很清楚了,就是入门篇。所以要讲的就是以下几点:
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下载地址)
- React Native Tools:微软官方出的ReactNative插件
- 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.pngapp.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
}
});