ReactNative APP开发学习之路

自述:

以前在北京工作时,有一次客户使用cordova开发,需要调用我们的原生的SDK。当初做了一个简单的cordova调用原生的插件,后来领导让我总结一下最近比较新的android开发技术。后来针对cordova、react Native、微信小程序做了一些相关的调研,这是我第一次接触react Native,被它的在线更新深深的折服。
    今年4月份,从北京辞职回武汉,进入的一家公司,他们做了一些项目,使用的是cordova开发,而我觉得cordova的技术和性能都不比react Native好。于是就在平时闲暇时间,做了一些react Native简单的调研,但是都没有成系统的去写点什么和做点什么。现在利用辞职和国庆的闲暇时间,打算稍微系统的学习一下React Native开发。

ReadingGank这个是我打算在学习React Native过程中,借鉴Reading项目,整合GankCamp-React-Native的应用。主要是想通过熟悉别人的开源项目,学习react-native中的一些用法,并做一些整合练习。

2017.9.27

熟悉redux

react-native 的数据传递是父类传递给子类,子类通过this.props.** 读取数据,这样会造成组件多重嵌套,于是用redux可以更好的解决了数据和界面View之间的关系, 当然用到的是react-redux,是对redux的一种封装。
reac基础的概念

1.Action是纯声明式的数据结构,只提供事件的所有要素,不提供逻辑,同时尽量减少在action中传递数据。
  2.Reducer是一个匹配函数,action发送的全局的,所有的reducer都可以捕捉到并匹配是否与自己相关的。相关的就拿走action的要素进行逻辑处理,修改store中的状态,不相关就不对state做处理,原样返回,reducer里就是判断语句。
  3.Store就是把以上两个联系到一起的对象,Redux应用只有一个单一的store。当需要拆分数据处理逻辑时,应该使用reducer组合,而不是创建多个store.
  4.Provider是一个普通组件,可以作为顶层app的分发点,它只需要store属性就可以了。它会将state分发给所有被connect的组件,不管它在哪里,被嵌套多少层。
  5.Connect 先接受两个参数(数据绑定mapStateTopProps和事件绑定mapDispatchToProps),再接受一个参数(将要绑定的组件本身)。mapStateToProps:构建好Redux系统时候,它会被自动初始化,但是你的React组件并不知道它的存在,因此需要分拣出你需要的Redux状态,所以需要绑定一个函数,它的参数是state,简单返回需要的数据,组件里读取使用this.props.*
  6.container只做component容器和props绑定,负责输入

ReactNative APP开发学习之路_第1张图片
Redux逻辑关系图

react-navigation导航库

npm install --save react-navigation

该库包含三类组件:
(1). StackNavigator:用来跳转页面和传递参数
(2). TabNavigator:类似底部导航栏,用来在同一个屏幕下切换不同界面
(3). DrawerNavigator;侧滑菜单导航栏,用于轻松设置带抽屉导航的屏幕

使用StackNavigator + TabNavigator实现Tab界面切换、界面间导航
API定义:

StackNavigator(RouteConfigs, StackNavigatorConfig);
TabNavigator(RouteConfigs, TabNavigatorConfig);

界面间跳转、传值、取值
在界面组件注入到StackNavigator中时,界面组件就被赋予了navigation属性,即在界面组件中可以通过【this.props.navigation】获取并进行一些操作。
(1).通过navigate函数实现界面之间跳转:

//Mine为我们在StackNavigator注册界面组件时的名称
this.props.navigation.navigate('Mine'); 

同样也可以从当前页面返回到上一页:

// 返回上一页  
this.props.navigation.goBack();  

(2).跳转时传值:

/**
 *第一个参数同样为要跳转的界面组件名称,
 *第二个参数为要传递的参数,info可以理解为key,后面即传递的参数。
 */
this.props.navigation.navigate('Mine',{info:'传值过去'});  

(3).获取值:

//通过state.params来获取传来的参数,info为key值
{this.props.navigation.state.params.info}  

参考文献
React Native未来导航者:react-navigation 使用详解

2017.9.29

添加redux相关库

npm install --save redux react-redux redux-saga
npm install --save-dev redux-logger

redux不用说了,我是把它当成一个本地数据库使用,react-redux帮助你完成数据订阅,redux-saga可以放你实现异步action,redux-logger是redux的日志中间件。

相比Redux Thunk,使用Redux Saga有几处明显的变化:

  • 在组件中,不再dispatch(action creator),而是dispatch(pure action)
  • 组件中不再关注由谁来处理当前action,action经由root saga分发
  • 具体业务处理方法中,通过提供的call/put等帮助方法,声明式的进行方法调用
  • 使用ES6 Generator语法,简化异步代码语法
  • 除开上述这些不同点,Redux Saga真正的威力,在于其提供了一系列帮助方法,使得对于各类事件可以进行更细粒度的控制,从而完成更加复杂的操作。

2017.10.4

  • redux-logger开发模式下使用说明
const middlewares = [];
if (process.env.NODE_ENV === `development`) {
  const { logger } = require(`redux-logger`);
  middlewares.push(logger);
}
const store = compose(applyMiddleware(...middlewares))(createStore)(reducer);

redux-saga API参考

  • 设置中间件三种方式
const store = createStore(reducer, null, applyMiddleware(...))
const store = createStore(reducer, applyMiddleware(...))
const store = applyMiddleware(...)(createStore)

参考文献
redux applyMiddleware 原理剖析

ES 6一些语法使用:

1. (...)操作符用于操作数组

展开运算符
作用和字面意思一样,就是把东西展开,可以用在array和object上都行。

let a = [1,2,3];  
let b = [0, ...a, 4]; // [0,1,2,3,4]  
  
let obj = { a: 1, b: 2 };  
let obj2 = { ...obj, c: 3 }; // { a:1, b:2, c:3 }  
let obj3 = { ...obj, a: 3 }; // { a:3, b:2 }  

剩余操作符
解构的一种,就是把剩余的东西放到一个array里面赋值给它。一般只针对array的解构。

let a = [1,2,3];  
let [b, ...c] = a;  
b; // 1  
c; // [2,3]  
  
// 也可以  
let a = [1,2,3];  
let [b, ...[c,d,e]] = a;  
b; // 1  
c; // 2  
d; // 3  
e; // undefined  
  
// 也可以  
function test(a, ...rest){  
  console.log(a); // 1  
  console.log(rest); // [2,3]  
}  
  
test(1,2,3) 

2.(=>)操作符
ES6匿名函数标识,也叫作箭头函数
3. (function)声明方式*
function* 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数,它返回一个Generator对象。

2017.10.10

Redux-saga常用方法解释

  • Redux Effects
    Effect是一个javaScript对象,可以通过yield传达给sagaMiddleware进行执行,如果我们应用redux-saga,所有的Effect都必须被yield才会执行。

举个例子,不使用saga:

yield fetch(url);

应用saga:

yield call(fetch, url);
  • take
    等待dispatch匹配某个action.
...
while(true){
        yield take('CLICK_Action');
        yield fork(clickButtonSaga);
}
...
  • put
    触发某个action,作用和dispatch相同;
yield put({type:'CLICK'});

具体的例子:

import { call, put } from 'redux-saga/effects'

export function* fetchData(action) {
   try {
      const data = yield call(Api.fetchUser, action.payload.url)
      yield put({type: "FETCH_SUCCEEDED", data})
   } catch (error) {
      yield put({type: "FETCH_FAILED", error})
   }
}
  • select
    作用和 redux thunk 中的 getState 相同。通常会与reselect库配合使用。

  • call
    有阻塞地调用 saga 或者返回 promise 的函数,只在触发某个动作

  • takeEvery
    循环监听某个触发动作,我们通常会使用while循环替代。

import { takeEvery } from 'redux-saga/effects'

function* watchFetchData() {
  yield takeEvery('FETCH_REQUESTED', fetchData)
}
  • takeLatest
    对于触发多个action的时候,只执行最后一个,其他的会自动取消。
import { takeLatest } from 'redux-saga/effects'

function* watchFetchData() {
  yield takeLatest('FETCH_REQUESTED', fetchData)
}
  • fork和cancel
    通常fork 和 cancel配合使用, 实现非阻塞任务。take是阻塞状态,也就是实现执行take时候,无法向下继续执行;fork是非阻塞的,同样可以使用cancel取消一个fork 任务。
function* authorize(user, password) {
  try {
    const token = yield call(Api.authorize, user, password)
    yield put({type: 'LOGIN_SUCCESS', token})
  } catch(error) {
    yield put({type: 'LOGIN_ERROR', error})
  }
}

function* loginFlow() {
  while(true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    yield fork(authorize, user, password)
    yield take(['LOGOUT', 'LOGIN_ERROR'])
    yield call(Api.clearItem('token'))
  }
}

上面例子中,当执行

yield fork(authorize, user, password)

的同时,也执行了下面代码,进行logout的监听操作。

yield take(['LOGOUT', 'LOGIN_ERROR'])

react-native-vector-icons配置

安装

Run: npm install react-native-vector-icons --save
For each platform (iOS/Android/Windows) you plan to use, follow one of the options for the corresponding platform.

Android

(1)在android/app/build.gradle中,添加下面配置

project.ext.vectoricons = [
    iconFontNames: ['Ionicons.ttf'] // Name of the font files you want to copy
]
 
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

dependencies{
    ...
    compile project(':react-native-vector-icons')
}

将相应字体(Ionicons.ttf)拷贝到android/app/src/main/assets/fonts目录。
(2)在android/settings.gradle添加如下两行

+ include ':react-native-vector-icons'
+ project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')

(3)在MainApplication.java添加如下

package com.readinggank;
 
+ import com.oblador.vectoricons.VectorIconsPackage;
 
....
 
  @Override
  protected List getPackages() {
    return Arrays.asList(
      new MainReactPackage()
+   , new VectorIconsPackage()
    );
  }

leancloud-storage数据存储

安装

Run: npm install leancloud-storage --save

引入 SDK 并初始化:

    import AV from 'leancloud-storage';  
    AV.initialize(APP_ID, APP_KEY);

react-native-wechat微信组件

安装
npm install react-native-wechat --save
Android配置

在android/settings.gradle文件下添加以下代码:

include ':RCTWeChat'
project(':RCTWeChat').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-wechat/android')

在android/app/build.gradle的dependencies部分添加以下代码:

dependencies {
    ...
    compile project(':RCTWeChat')    // Add this line only.
}

在MainApplication.java文件中添加以下代码:

import com.theweflex.react.WeChatPackage;     
...

/**
 * A list of packages used by the app. If the app uses additional views
 * or modules besides the default ones, add more packages here.
 */
@Override
protected List getPackages() {
  return Arrays.asList(
    new MainReactPackage(), 
    new WeChatPackage()        // Add this line
  );
}

在应用程序包中创建一个名为'wxapi'的包,并在其中创建一个名为'WXEntryActivity'的类。以便可以获得微信的授权和分享权限。

package your.package.wxapi;

import android.app.Activity;
import android.os.Bundle;
import com.theweflex.react.WeChatModule;

public class WXEntryActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    WeChatModule.handleIntent(getIntent());
    finish();
  }
}

在AndroidManifest.xml添加声明


  
    
  

React.Component

组件的生命周期方法

前缀为Will的生命周期方法会在在一些事情发生之前被调用,带有Did前缀的方法在某些事情发生之后被调用。

Mounting(加载组件)
当创建组件的实例并将其插入DOM时,会依次调用这些方法:

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

Updating(更新状态)
更新可以由prop或者state的改变引起。在重新渲染组件时依次调用这些方法:

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

Unmounting(卸载组件)
当从DOM中删除组件时,将调用此方法:

  • componentWillUnmount()

Animated动画库

ReactNative APP开发学习之路_第2张图片
Animated知识点

bindActionCreators

Redux中的bindActionCreators,是通过dispatch将action包裹起来,这样可以通过bindActionCreators创建的方法,直接调用dispatch(action)(隐式调用)。

connect

connect方法声明:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])

作用:连接React组件与 Redux store。

react-native-scrollable-tab-view

安装
npm install react-native-scrollable-tab-view --save

react-native-timeago

安装
npm install react-native-timeago --save

简单使用

import React, { Component } from 'react';
import TimeAgo from 'react-native-timeago';

// Timestamp can be any valid data type accepted in a Moment.js constructor
// Currently accepts string, number, array, or a Date instance
let timestamp = "2015-06-21T06:24:44.124Z";

class MyComponent extends Component {
  ...
  render() {
    return (
      
    )
  }
  ...
};

参考文献
(1). Redux-Saga在React工程架构之的应用实践详解
(2). react-native-wechat微信组件的使用
(3). React从入门到精通系列之(22)React.Component用法
(4). React Native进阶之Animated动画库详解-基础篇(64)
(5). Redux中的bindActionCreators
(6). 关于react-redux中的connect用法介绍及原理解析
(7). React Native之react-native-scrollable-tab-view详解

2017.10.11

Lodash

moment

注意事项

  1. Genymotion调试注意设置Android SDK为本地,不要使用genymotion默认SDK
  2. 如果出现could not connect to develpment server错误,请确定Node.js是否正常启动

你可能感兴趣的:(ReactNative APP开发学习之路)