前言
從最早接觸react native也快接近一年了,不多不少的也做了有3個項目了,但是技術好像沒有什么提升誒(??????),其中很有感觸的是在開發一個收入的項目的時候,做下來發現文件太多了,不好管理,根據問題查看代碼很是膈應。不過還好的是最近接觸到了一個叫dva的前端框架(聽說支付寶前端團隊開發的框架),dva是出自于守望先鋒游戲的一個角色 => D.Va擁有一部強大的機甲,裝備了各種高科技武器。同樣dva框架呢是對redux+saga這種方式管理數據流的整合封裝,目的很簡單,讓使用者更簡單的,更方便的管理數據流。
dva的作用
從代碼結構管理層面
以前的項目就是使用原生的redux管理的,當然還有處理異步操作的saga,所以針對一個業務點,代碼會分布在很多文件中。
下圖,則是通過dva來管理的react native項目,action,reducer,saga都放在model模塊,相對簡潔很多。
其中model的編寫是dva的核心。
從代碼編寫繁瑣程度
redux store 的創建,actionCreater的創建,中間件的配置,路由的初始化,Provider 的 store 的綁定,saga 的初始化,還要處理 reducer, component。
基于上面的這些問題,封裝了 dva 。dva 是基于 redux 最佳實踐 實現的 framework。
dva接入的課前輔導
簡單畫下我對Redux,Redux-sage 整個流程的理解。
-
Redux
image.png -
Redux-Saga
image.png
react native+dva+react-navigation 的一個demo
項目結構如下圖
接下來我們就從零搭建,一定要動手去敲哦!!!??
通過dva初始化根頁面
react native 的默認初始化方式, 第二個參數是Component類型
AppRegistry.registerComponent('XXXApp', () => XXXAppComponent)
but,right now
index.ios.js
import {
AppRegistry
} from 'react-native'
import app from './src'
AppRegistry.registerComponent('ReduxTest', app)
這個app是個什么東東呢,先賣個關子??
app.js
import React from 'react'
import dva, { connect } from 'dva/mobile'
import { registerModels } from './models'
import Router from './routes'
// 1. Initialize
const app = dva()
// 2. Model
registerModels(app)
// 3. Router
app.router(() => <Router />)
// 4. Start
export default () => {
return app.start()
}
其中步驟2中的注冊model,可以先不用care,重點放在后兩個步驟,Router是個什么東西呢,你可以簡單的理解為RootComponent,我們一般開發react native的RootComponent即為TabNavigator,StackNavigator,該demo以StackNavigator為根頁面,所以呢,我們就簡單的將其導出為Router,然后注冊到dva中,app.start()
將會啟動應用,并返回一個Component。這也很好的解釋了AppRegistry中注冊app.start()
返回的Component。
Router ???
router.js
import {
StackNavigator,
addNavigationHelpers
} from 'react-navigation'
import React, { Component } from 'react'
import { BackHandler, Animated, Easing } from 'react-native'
import { connect } from 'dva'
import Login from '../pages/Login'
import Profile from '../pages/Profile'
const AppNavigator = StackNavigator(
{
Login: {screen: Login},
Profile: {screen: Profile}
},
{
navigationOptions: {
gesturesEnabled: true,
},
}
)
@connect(({ router }) => ({ router }))
export default class Router extends Component {
render() {
const { dispatch, router } = this.props
const navigation = addNavigationHelpers({ dispatch, state: router })
return <AppNavigator navigation={navigation} />
}
}
export function routerReducer(state, action = {}) {
return AppNavigator.router.getStateForAction(action, state)
}
Router主要是簡單定義了下StackNavigator中的存放的Component,默認第一個為RootComponent 即Login。需要解釋一下的是@connect(({ router }) => ({ router }))
這是es7的語法,有興趣可以google下,這里我只把router數據給傳進來,addNavigationHelpers({ dispatch, state: router })
是將會在執行navigation.goBack()
,navigation.navigate()
的同時執行對應的dispatch,更新router數據。
export function routerReducer
這個外部接口, 提供外部獲取路由信息。
Login.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
TouchableOpacity,
Text,
View
} from 'react-native'
import { connect } from 'dva'
import {
NavigationActions
} from 'react-navigation'
@connect(
appNS => ({ ...appNS }),
{
increase: () => (({ type: 'appNS/add' })),
login: () => (({ type: 'appNS/login' }))
}
)
export default class Login extends Component {
static navigationOptions = {
title: '登錄頁',
}
goLogin() {
this.props.login()
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.loginButton} onPress={() => this.goLogin()}>
<Text style={styles.loginLabel}>登錄</Text>
</TouchableOpacity>
</View>
);
}
}
like this
其中點擊登錄按鈕會觸發dispatch({ type: 'appNS/login' })。接下來我們就來看看重中之中針對這個頁面的model編寫。
models/app.js
import { NavigationActions } from '../tools'
import { createAction } from '../tools'
import { get, post } from '../tools/fetch'
export default {
namespace: 'appNS',
state: {
isLogin: false,
userName: '路人甲',
loginFailedReason: 'no reason',
count: 0
},
reducers: {
add(state, { payload }) {
return {
...state,
count: (state.count + 1)
}
},
loginSuccessed(state, { payload }) {
return {
...state,
isLogin: true,
userName: payload.userName
}
},
loginFailed(state, { payload }) {
return {
...state,
isLogin: false,
loginFailedReason: payload.loginFailedReason
}
}
},
effects: {
*login(payload, { put, call }) {
// yield put({ type: 'loginSuccessed', {'name': 'yellow'} })
// yield put(createAction('loginSuccessed')({'name': 'yellow'}))
try {
const res = yield call(() => get('https://httpbin.org/get'))
if (res.url) {
yield put(createAction('loginSuccessed')({'userName': 'yellow'}))
yield put(NavigationActions.navigate({ routeName: 'Profile'}))
} else {
yield put(createAction('loginFailed')({'loginFailedReason': '賬號密碼錯誤'}))
}
} catch (e) {
console.log(e);
}
}
}
}
首先,簡單說明下model 就是一個大的json對象,其中有幾個重要的key。namespace,當你connect一個Component就是通過這個值來連接的,以及跨model調用action,ex:
put({type:'namespace/xxaction'})
,state放置一些初始化或是需要維護的數據。reducers里就放一些action對應的純函數,修改state數據源。effects存放一些網絡請求,I/O操作的有副作用的方法,其中會調用reducer的方法,從而改變數據源。
幫助大家理一下流程
page/this.props.login() ——》effects/*login ——》success? reducer/loginSuccessedAction——》state/isLogin: true
effects中有兩個比較常用的輔助函數
put
,call
,put函數調用一個action,call用于調用異步邏輯,支持 promise
如何在effects中進行頁面的跳轉呢?
以前我遇到這個問題也很頭疼,就用了一個很暴力的方法,用global全局對象來保存Navigator,然后來進行操作。但是react-navigation這個第三方組件,既支持UI層面的頁面切換,也支持對redux的接入(路由信息的獲取和修改,修改也會影響到UI)
models/router.js
import { createAction, NavigationActions } from '../tools'
import { routerReducer } from '../routes'
const watcher = { type: 'watcher' }
const actions = [
NavigationActions.BACK,
NavigationActions.INIT,
NavigationActions.NAVIGATE,
NavigationActions.RESET,
NavigationActions.SET_PARAMS,
NavigationActions.URI,
]
export default {
namespace: 'router',
state: {
...routerReducer(),
},
reducers: {
apply(state, { payload: action }) {
return routerReducer(state, action)
},
},
effects: {
watch: [
function*({ take, call, put }) {
while (true) {
const payload = yield take(actions)
yield put(createAction('apply')(payload))
if (payload.type === 'Navigation/NAVIGATE') {
console.log('11111',payload);
}
}
}, watcher]
},
}
其實就做了兩件事,通過之前的Router組件中提供的獲取路由信息初始化到state中,effects中監聽NavigationActions,然后調用apply來更新路由信息,最后又因為我們將路由信息鏈接到Router組件,所以就會有UI頁面的切換。
二維碼地址
總結來說,dva雖然屏蔽了redux和saga的一些細節,但你要真正運用到項目中,還是需要惡補下這方面的知識,前端框架變化莫測,如何擁有一個自學的方法是很關鍵的,以及學習的及時反饋,對于新人來說一劑強力的助推器。