ReactNative实战
一、环境准备
注*:下面习惯将ReactNative简称为RN
详细步骤可参见 -> RN环境搭建
node
、yarn
,开发工具android studio
和vscode
需要用到react-native
脚手架搭建项目,yarn global add react-native
创建RN项目,react-native init app-demo
,可以看到当前项目生成了一个app-demo目录,里面有android目录和ios目录,分别表示android开发和ios开发的,因为RN实际上只是是跨平台的UI库,大多数涉及到系统开发的逻辑需要通过与原声交互的方式实现
通过命令行进入app-demo
目录,使用yarn install
下载依赖,命令会检索package.json
文件中的dependencies
和devDependencies
节点的依赖下载到当前目录的node-modules
文件夹下
常见问题(一)
直接启动RN项目在下载android相关依赖时可能会因为网络问题导致下载包失败
设置android依赖设置为国内镜像,提高下载速度,打开android
目录,编辑build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
// googlePlayServicesVersion = "16.1.0" // default: "16.1.0" - pre-AndroidX, override for AndroidX
}
repositories {
// google()
// jcenter()
// 替换成国内镜像源
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/google' }
}
dependencies {
classpath('com.android.tools.build:gradle:3.5.2')
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
// 先本地maven仓库中找
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://jitpack.io' }
// google()
}
}
常见问题(二)
这样直接运行RN项目可能会遇上一个奇怪的bug,页面直接报错,Cannot find entry file index.android.js in any of the roots
,原因是RN默认是去找index.android.js这个文件,而初始化的RN项目的入口文件名叫index.js,所以需要修改android/app/build.gradle
project.ext.react = [
entryFile: "index.js",
enableHermes: false, // clean and rebuild if changing
bundleInDebug: true
]
运行RN项目
打开package.json
文件,找到scripts
节点,目前只关心android
和start
命令,可通过yarn android
运行scripts
节点下的命令,相当与间接运行react-native run-android
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
手机进入开发者模式,并设置连接方式为文件传输,运行adb devices
命令确保设备已正常运行并连接,运行yarn android
安装应用。
start
命令会启动一个守护进程,用来监控app,可以实现app的热更新,通过r
键刷新app,d
在app上打开调试菜单
二、ReactNative是什么
React有着Learn One, Write Any的名号,RN也不例外,它完全可以用React的语法进行编码开发
对于使用过React的人并不陌生,render函数是React在渲染页面时使用的方法,对于RN来说,render函数也是渲染app页面的重要函数
但React和RN在render函数中的使用规则有着比较大的区别,就是React在浏览器中使用时,可以在render函数中使用任何标签,例如<div></div>
等,而RN中只能使用预定义好的标签,比如<View></View>
,在RN中,可以把View标签当作div来用
之所以不能使用普通的html标签是因为RN和普通的WebView实现的app不一样,它需要保证App的性能,所以需要做一些定制化工作,比如View这些标签实际上在android或ios有着自己原生代码的实现,就拿android来说,RN在实现View标签的时候,在android底层封装了一个active.xml,使用View标签就相当与在底层使用了这个active.xml的布局文件,所以RN的性能并不会和原声Android相差太多
因为RN的UI组件都是经过原生代码封装过的,所以它无法想WebView那样做到任意的定制化布局,它的想象空间被极大的限制住了。但是RN还是有着自己独特的优点,它能让不熟悉原生android的前端开发人员也能具备开发一些简单的“Curd App”的能力,然并luan!如果想用RN开发出来一款真正的产品,仅仅是写写页面是微不足道的,他离不开原生开发的支持。比如做个百度地图需要用到GPS,登录页的人脸识别需要相机,需要在AndroidManifest.xml文件中配置app所需要的权限,比如接下来我要讲的硬件监控需要读取android系统的/proc/stat(CPU使用情况)或者/proc/meminfo(内存使用情况)等。直接用RN是做不到的,虽然网上有类似的插件(react-native-device-info
),但我测试的时候发现获取内存信息和磁盘信息数据量不准确的问题,还有不支持获取cpu和流量信息。
可以看到,当想要把app真正的作为一款产品来开发的话,绝对是离不开原生开发的支持的,就算RN发展的如火如荼,但不管怎样,毕竟Android和ios才是地头蛇,如果只是一味的想要比较WebView、RN、原声这些app开发技术到底哪个好,最终只会是限制自己的发展
三、使用RN的第三方插件
大多数RN的第三方插件也是通过原生代码实现的
以上面提过的react-native-device-info
为例,其它的RN插件引入RN的方式和这个一样,像react-native-fs
(文件读写)、react-native-background-timer
(后台定时任务,虽然用RN的Headless也可以实现)这些插件,能提高我们的开发效率,当然,如果经历允许的话,也可以自己编写插件并发布到网上。
可能插件官方的教程适用于旧版RN,对于我0.61.5较新的版本来说,需要稍作改动
-
yarn add react-native-device-info
-
修改
android/settings.gradle
,添加include ':react-native-device-info' project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
-
修改
android/app/build.gradle
,在dependencies节点添加implementation project(':react-native-device-info')
然后就可以直接在js上使用了
四、RN与原生交互
创建一个简单的工具类,然自己可以通过js调用android的代码,工具类需要继承ReactContextBaseJavaModule
并实现getName
方法,getName
方法返回的字符串就是工具类的标识
下面定义的toastMsg
方法接收js传过来的参数字符串并将参数作为提示弹出到app页面上
提供给js调用的方法需要加上@ReactMethod
注解
在js中可以通过NativeModules.ToastExample.toastMsg("提示")
这种方法调用
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@NonNull
@Override
public String getName() {
return "ToastExample";
}
/**
* 弹出一个提示
*/
@ReactMethod
public void toastMsg(String msg) {
Toast.makeText(getReactApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
第二步,还需要将自定义的工具类注册一下,再创建个自定义的包管理器,实现ReactPackage
接口
public class AnExampleReactPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
// 将刚刚的工具类放上去
modules.add(new ToastModule(reactContext));
return modules;
}
}
第三步,修改MainApplication.java
文件,在ReactNativeHost
内部类实现的getPackages
方法中加上刚刚的自定义包管理器
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// 将自定义包管理器放进去
packages.add(new AnExampleReactPackage());
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
大功告成!