React-Native Android
既拥有Native的用户体验、又保留React的开发效率。
尽管Native开发成本更高,但现阶段Native仍然是必须的,因为Web的用户体验仍无法超越Native:
① Native的原生控件有更好的体验;
② Native有更好的手势识别;
③ Native有更合适的线程模型,尽管Web Worker可以解决一部分问题,但如图像解码、文本渲染仍无法多线程渲染,这影响了Web的流畅性。
④ Native能实现更丰富细腻的动画效果,归根结底是现阶段Native具有更好的人机交互体验
1.环境
React Native只支持在OS X系统,React Native开发的app 要求客户端os >= Android 4.1 (API 16) 和>= iOS 7.0
1.1 工具
- Homebrew
- Nodejs
- nvm
- watchman
- flow
1.2 SDK 环境要求
- Android SDK Build-tools version 23.0.1
- Android 6.0 (API 23)
- Android Support Repository
1.3 Demo Hello World
- $ npm install -g react-native-cli
- $ react-native init AwesomeProject
- $ cd AwesomeProject/
- $ react-native run-android
相应的会在目录AwesomeProject/android/app下创建Android Studio工程,AwesomeProject/iOS/AwesomeProject.xcodeproj目录中创建XCode工程
注:不管是 iOS 还是 Android,在开发调试阶段,都需要在 Mac 上启动一个 HTTP 服务,称为
Debug Server
,默认运行在 8081 端口,APP 通 Debug Server 加载 js。
2.概念
2.1 Component组件
React Native 主要是通过 Virtual Dom 来实现显示页面或者页面中的模块。可以通过 React.createClass() 来创建自己的 Dom,在 React 中称之为组件(Component)
1. 创建组件
- // Android
- var React = require('react-native');
- var { DrawerLayoutAndroid,ProgressBarAndroid } = React;
-
- var App = React.createClass({
- render: function() {
- return (
- <DrawerLayoutAndroid
- renderNavigationView={() => <Text>React Native</Text>}>
- <ProgressBarAndroid />
- </DrawerLayoutAndroid>
- );
- },});
-
- // iOS
- var React = require('react-native');
- var { TabBarIOS,NavigatorIOS } = React;
-
- var App = React.createClass({
- render: function() {
- return (
- <TabBarIOS>
- <TabBarIOS.Item title="React Native" selected={true}>
- <NavigatorIOS initialRoute={{ title: 'React Native' }} />
- </TabBarIOS.Item>
- </TabBarIOS>
- );
- },});
2. 使用组件
类似 HTML 标准标签
<< MyCustomComponent />
或
<< MyCustomComponent >
<< /MyCustomComponent>
3. 组件生命周期
React 组件的数据保存在自己内部的 state 变量中.都有相应回调。
- getInitialState: 获得初始化组件状态,只调用一次.
- componentWillMount: 组件将要加载,只调用一次
- componentDidMount: 组件加载完成并显示出来了,也就是完成了一次绘制,只调用一次
- render: 绘制组件,可能调用多次。
4. 自定义组件
render 函数是必须的,其他可选
- var MyCustomComponent = React.createClass({
-
- getInitialState: function() {
- // 这里返回一个对象,设置组件的初始化状态,后面就可以通过 this.state 来获得这个对象
- return {
- key1: data1,key2: data2,...
- };
- },componentWillMount: function() {
- // 这里一般做一些和界面显示无关的初始化操作
- },componentDidMount: function() {
- // 这里一般做加载数据的操作
- },render: function() {
- // 这是最重要的函数,用来绘制界面,所有的自定义组件,这个函数是必须提供的
- return(
- <View>
- ...
- </View>
- );
- },});
5. 组件数据
根据组件的状态 state 来绘制动态页面
- render: function() {
- return(
- //把状态中的 key1 的值用 Text 组件直接显示
- <Text>{this.state.key1}</Text>
- );
- }
状态(statu)
组件的状态(statu) 除了使用 getInitialState 方法来设置初始化状态外,在界面逻辑处理或者事件交互的过程中,
可以调用 this.setState(…) 方法来修改组件的状态值。如果在代码中直接修改 state,React 就会把旧状态和新状态
做一个 diff,找到变化的部分,然后对应找到和这个变化的值关联的界面部分,请求重新绘制这个部分。
属性(Property)
属性(Property)可以通过 this.props 来直接获取
- <View style={{flex: 1}}>
区别: 一般 属性 表示静态的数据,组件创建后,就基本不变的内容,状态 是动态数据。
3.React Native布局
React Native 的布局,实用的是 FlexBox 实现,类似网页的 CSS 布局方法
React Native 中的样式长度单位,是逻辑单位,概念和 Android 中的 dp 一样。
示例Demo: 知乎日报客户端
github源码地址
4.Android Studio示例工程概览
MainActivity 已经针对React Native做了一层封装调用,默认帮我们维护了React Native的生命周期。
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.KeyEvent;
-
- import com.facebook.react.LifecycleState;
- import com.facebook.react.ReactInstanceManager;
- import com.facebook.react.ReactRootView;
- import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
- import com.facebook.react.shell.MainReactPackage;
- import com.facebook.soloader.SoLoader;
-
- public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
-
- private ReactInstanceManager mReactInstanceManager;
- private ReactRootView mReactRootView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mReactRootView = new ReactRootView(this);
-
- mReactInstanceManager = ReactInstanceManager.builder()
- .setApplication(getApplication())
- .setBundleAssetName("index.android.bundle")
- .setJSMainModuleName("index.android")
- .addPackage(new MainReactPackage())
- .setUseDeveloperSupport(BuildConfig.DEBUG)
- .setInitialLifecycleState(LifecycleState.RESUMED)
- .build();
-
- mReactRootView.startReactApplication(mReactInstanceManager,"AwesomeProject",null);
-
- setContentView(mReactRootView);
- }
-
- @Override
- public boolean onKeyUp(int keyCode,KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
- mReactInstanceManager.showDevOptionsDialog();
- return true;
- }
- return super.onKeyUp(keyCode,event);
- }
-
- @Override
- public void invokeDefaultOnBackPressed() {
- super.onBackPressed();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- if (mReactInstanceManager != null) {
- mReactInstanceManager.onPause();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- if (mReactInstanceManager != null) {
- mReactInstanceManager.onResume(this);
- }
- }
- }
index.android.js
- **
- * Sample React Native App
- * https://github.com/facebook/react-native
- */
- 'use strict';
-
- var React = require('react-native');
- var {
- AppRegistry,StyleSheet,Text,View,} = React;
-
- var AwesomeProject = React.createClass({
- render: function() {
- return (
- <View style={styles.container}>
- <Text style={styles.welcome}>
- Welcome to React Native!
- </Text>
- <Text style={styles.instructions}>
- To get started,edit index.android.js
- </Text>
- <Text style={styles.instructions}>
- Shake or press menu button for dev menu
- </Text>
- </View>
- );
- }
- });
-
- var 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,});
-
- AppRegistry.registerComponent('AwesomeProject',() => AwesomeProject);
package.json 是工程的依赖和元数据配置文件:
- {
- "name": "AwesomeProject","version": "0.0.1","private": true,"scripts": { "start": "node_modules/react-native/packager/packager.sh" },"dependencies": { "react-native": "^0.11.0" } }
5.打包发布独立安装包
开发调试的时候必须启动个 JS Server,然后要让手机连接这个 Server。React Native 应用打包的时候,会连接 JS Server 下载一个 ReactNativeDevBundle.js 文件,然后放到应用数据的 files 目录下,就能运行这个 JS 文件了
目前 iOS 应用可用 react-native bundle 命令进行打包,android暂时没有支持。
针对 Android 的 React Native 应用,可用 react-native-gradle 插件进行打包。该插件灵活配置打包参数,使用 Gradle Task 将资源打包到资源文件夹。
5.1 react-native-gradle 插件
借助插件 react-native-gradle:com.facebook.react:gradleplugin:1.0.+ 可完成混淆及资源打包。(插件并没有发布到 JCenter 或者 Maven Centry)插件源码github地址
安装: react-native-gradle srain$ gradle install
项目中使用build.gradle配置:
- buildscript {
- repositories {
- mavenLocal() // 本地依赖
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
- classpath 'com.facebook.react:gradleplugin:1.0.+' // 插件依赖
- }
- }
app/build.gradle配置:
- apply plugin: 'com.facebook.react'
-
- react {
- bundleFileName "index.android.bundle" // assets 目录下 js 文件名
- bundlePath "/index.android.bundle" // js 路径
- jsRoot "../" // js 源文件位置
- packagerHost "localhost:8081" // debug server 地址
- packagerCommand "./node_modules/react-native/packager/packager.sh" // 打包命令地址
-
- devParams {
- skip true
- dev true
- inlineSourceMap false
- minify false
- runModule true
- }
- releaseParams {
- skip false
- dev false
- inlineSourceMap false
- minify true
- runModule true
- }
- }
Params参数:
- skip 是否跳过从本地资源加载,从 Debug Server 请求资源
其他四个参数通过 url 传给 Debug Server。
- dev: 全局变量 DEV,React Native 核心类库的开发选项
- minify: 混淆
- inlineSourceMap: 是否加入 source map。默认 false
- runModule: 是否在最后以 require(XXX) 的形式加入 module 的入口点。默认 true
require(“AwesomeProject/index.android.js”);
官网地址:https://facebook.github.io/react-native/