自述:
以前在北京工作时,有一次客户使用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绑定,负责输入
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动画库
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
注意事项
- Genymotion调试注意设置Android SDK为本地,不要使用genymotion默认SDK
- 如果出现could not connect to develpment server错误,请确定Node.js是否正常启动