React-Native是FaceBook在2015年推出的一个能够使用javascript语言开发原生App的框架(A framework for building native apps with React)。为何叫做React-Native呢?个人理解是因为该框架的js代码部分是基于React.js这个开源组件的,用React.js开发native应用(React Native lets you build mobile apps using JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.)
学习RN所涉及到的一些前置基础
- ES6标准的js:RN的开发语言是js,而且是基于ES6标准的js语法。所以如果你之前不是做前端的,而是做ios或者安卓开发(比如像我这样),那么需要你对ES6标准的js语法有大致了解。不然后面分析官方demo的js部分的时候会一脸懵逼的。不用了解的很深入,因为细节部分可以随着你自己实际工作遇到再深入研究。ES6标准的js教程推荐大家看:http://es6.ruanyifeng.com/#docs/module 和https://blog.csdn.net/qq_27552077/article/details/72902926
- React.js:前面说过RN的js部分是基于React.js。而React.js可以看作是一个前端UI组件化的库,而且React.js和js不同的是React.js可以在js代码中插入html标签。实际上这种新的语法叫做JSX。关于React.js和JSX介绍推荐大家看http://huziketang.mangojuice.top/books/react/lesson1。写的超级棒,作为一个几乎从没接触过js,一直搞安卓开发的我一看就能明白,很少看到介绍原理性技术文章能写的这么好的教程了,而且作者讲解React.js是从一个js的demo开始,然后不断封装重构为组件化形式。这对你后面分析官方demo的js部分很有帮助。所以如果你想学习了解React.js的话推荐你一定要看下。
虽然在这里说了学习RN需要的一些前置基础,但我不推荐大家先学习这2个知识点之后再来学习RN(除非你本身就是做前端的)。而是推荐大家先分析官方demo,然后在不明白的地方回过头来从那2个知识点处学习。原因还是我一直强调的学习要带有目标感。带着某个疑惑去学习,学习的目的就是从你打算学习的知识点中去寻找能够解决你当前疑惑的方法。当你了解完React.js之后如果这个疑惑没有了就说明学习有效果。也就是用输出倒逼输入。不然的话对于这种语法性和原理性的知识点直接去看的话很容易走马观花,抓不到重点,过后即忘。
官方demo的获取
前面说过RN是使用js语言进行开发,所以你的机器上需要有能够运行js的环境,即安装node.js环境。关于环境搭建这块就不说了。大家直接参看官方文档,按照官网步骤一步一步来就行了。官方文档地址:https://facebook.github.io/react-native/docs/getting-started.html
搭建好环境之后,选择一个路径,然后执行如下命令来获取官方demo工程
react-native init AwesomeProject
注意:该命令还可以通过--version(前面是2个-)参数指定使用的RN的版本号,例如react-native init AwesomeProject --version 0.55.4 。 --version这个参数很重要,因为可能默认版本0.56在某些情况下会存在很难bug,那么这个时候可以考虑换一个版本(我自己测试时0.56版本就出现过一个很难解决的问题,在StackOverflow上有歪果仁遇到过同样问题,使用0.55.4版本就没这个问题)
该命令正确执行完成之后会在当前路径下生成一个名为AwesomeProject的工程目录,该目录即是FaceBook的RN的官方demo工程代码,目录结构如下所示:
其中比较重要的几个目录就是上图圈红的几个
- android:RN的安卓原生工程
- ios:RN的ios原生工程
- node_modules:js模块库(其中就包含react-native的RN模块),因为RN是使用js开发,所以需要js的基础库,就像python开发需要python库支持一样。
- App.js:可以认为是RN代码UI组件模块
- index.js:可以认为RN代码执行时的入口,一般会在该文件中进行js模块注册。这个模块名称(即index)要和安卓原生代码中的ReactApplication的getJSMainModuleName方法返回值保持一致
- package.json:js模块的项目配置文件,比如项目依赖的react和react-native的版本号等
然后执行如下2个命令即可自动编译安卓上的apk。
cd AwesomeProject
react-native run-android
编译常见问题解决
在执行完react-native run-android命令编译完成之后,安装apk到手机上如果界面出现红色错误,提示如下信息:
unable to load script from assets index.android.bundle
原因:这是因为RN虽然是使用js开发,但是RN不是一个简单的web app或者h5 app,而是一个真正的原生的app。所以RN并不是简单的把App.js和index.js打包进apk,然后运行,而是会在编译过程中把js文件转换为bundle文件。在运行的时候会从apk的assets目录下加载这些bundle文件。所以需要提供bundle文件。
解决方案:首先在项目的android/app/src/main/目录下创建一个assets目录。然后执行如下命令:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
如果命令行输处Done writing bundle output表明该命令被正确执行完成,此时命令行输出类似如下所示:
命令正确执行之后会在android/app/src/main/assets目录下生成一个index.android.bundle的文件和与之相对应的meta文件。然后重新执行react-native run-android即可。
如果命令未能被正确执行,出现Unable to resolve module 'AccessibilityInfo'请参看下面对应的解决方法。
官方demo代码分析
官方demo包含2部分,一部分是RN层的js代码,一部分是安卓原生的java代码。先分析下RN部分,即index.js和App.js。
首先分析下js入口index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('AwesomeProject', () => App);
这里的代码就是ES6标准的js语法,import的作用和python中的import类似,主要是引入其他js模块,第二个import语句就是引入了App.js这个模块。AppRegistry.registerComponent('AwesomeProject', () => App);这句就是注册了一个叫做App的组件,将该组件作为AwesomeProject的根组件。这是RN中js的一个API,其主要作用就是将第二个参数传入的js组件作为第一个参数传入的字符串对应的安卓原生界面的根组件(第一个参数的值需要和安卓java代码中继承自ReactActivity的原生Activity中getMainComponentName返回值保持一致,这在后面的官方demo安卓部分代码分析中会进一步讲到),关于该API更详细信息可以参看官方文档https://facebook.github.io/react-native/docs/appregistry。
()=>是js中的箭头函数语法,可以理解为无参的匿名函数。不清楚的可以查阅:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
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,
},
});
这里的语法就是JSX的语法,即可以将html标签和css格式与js代码混在一起使用。不清楚的可以回过头去看下我最开始提到的学习RN需要知道的几个前置知识点。
export default class App extends Component<Props>
这里的继承语法和java类似,是ES6标准的js语法,后面的Component时React.js的一个组件类,一般我们写的该组件都会继承该类,然后重写该组件的render,在该方法中返回我们要显示的UI视图即可。可以看到官方的dmeo中简单的返回了一个View视图,该View视图包括3个文本控件,然后通过css样式为这3个文本控件指定UI属性。比如对齐属性,字体大小,字体颜色等。
RN的js部分的代码就分析完了,接下来看下demo的android的原生部分代码,也很简单就只包括2个类文件MainActivity.java和MainApplication.java。因为安卓App最先执行的是Application。所以先看下MainApplication。
package com.awesomeproject;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
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()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
实现RN中的ReactApplication接口,创建了一个ReactNativeHost的内部类,然后在重写的getReactNativeHost方法中返回该内部类对象。该内部类中有一个很重要的方法getJSMainModuleName,其返回值即是app运行后加载js代码时的入口模块名称。比如官方demo是index.js。所以返回值是index。
package com.awesomeproject;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "AwesomeProject";
}
}
这个就更简单了,只是继承自ReactActivity,然后重写了其getMainComponentName方法,返回了一个字符串内容。然后从该方法的注释上可以看到其作用是返回从js中注册的主组件的名称,用于调度组件渲染。这里返回的字符串的内容即是index.js中调用AppRegistry.registerComponent('AwesomeProject', () => App);注册时的内容。也即用该API的第二个参数传入的js组件作为第一个参数所代表的安卓界面的根组件。从而知道安卓java层的Activity对应的根视图是js层的哪个js组件。




文章评论