dva之React Naitve中的戰鬥攻略
前言
從最早接觸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
-
Redux-Saga
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的一些細節,但你要真正運用到專案中,還是需要惡補下這方面的知識,前端框架變化莫測,如何擁有一個自學的方法是很關鍵的,以及學習的及時反饋,對於新人來說一劑強力的助推器。
最後上一張我家貓咪生的小寶寶 嘻嘻
相關文章
- react前端框架dva(三)React前端框架
- react前端框架dva(四)React前端框架
- 【乾貨】TypeScript 實戰之 extends、infer 與 dva typeTypeScript
- react+dva+antd介面呼叫方式React
- dva應用中reducers和effects的單元測試實戰
- Dva手腳架搭建React專案React
- React生態,dva原始碼閱讀React原始碼
- React服務端渲染實現(基於Dva)React服務端
- dva 中的響應程式設計程式設計
- GRE考試中的Argument寫作與實戰攻略
- 遊戲戰鬥的設計分析遊戲
- 建立衝突:AAA遊戲的戰鬥設計之難度控制遊戲
- Vue vs React: Javascript 框架之戰VueReactJavaScript框架
- 解構遊戲戰鬥:戰鬥元素分解與設計原則遊戲
- react-dva學習 --- 用例項來入門React
- React.js 實戰之 元素渲染ReactJS
- React狀態管理大亂鬥,橫向對比Dva,Rematch,MirrorReactREM
- dva-boot[-X] React相關工程自動配置工具bootReact
- "跳躍"在遊戲中的運用,多樣化關卡和戰鬥技能,唯一的信仰之躍遊戲
- 無法超越的怪獵系列,關於共鬥遊戲的戰鬥思考遊戲
- 【GDC 21】《對馬島之魂》戰鬥系統講解
- react-navigation圖文攻略ReactNavigation
- 深度解析戰鬥通行證
- 新預告展示戰鬥/合作元素
- 戰鬥解析完成!「#COMPASS戰鬥天賦解析系統」公測開啟
- 如何做好回合制遊戲的戰鬥體驗? 戰鬥數值公式設計詳解遊戲公式
- 從《劍與家園》中學習戰鬥策略設計
- 動作遊戲戰鬥系統總結歸納&思考(中)遊戲
- React.js 實戰之 事件處理ReactJS事件
- REMOD模組化錶帶登入眾籌 錶帶中的戰鬥機REM
- 談談戰雙的戰鬥機制設計趨同
- 《碧藍幻想:Versus》的創新戰鬥系統
- 遊戲中戰鬥的分層結構(上):長線遊戲的“長壽”祕訣遊戲
- 減少失誤提高策略性,ACT遊戲中的戰鬥資源控制遊戲
- 【商場如戰場】IT人才外包創業過程中的鉤心鬥角創業
- React.js 實戰之 JSX 簡介ReactJS
- 遊戲戰鬥力數值研究(一)遊戲
- 談談ACT手遊戰鬥系統