dva是一個在redux和redux-saga的基礎上封裝的一個輕型框架,能輔助更好的組織代碼進行開發。同時提供了react-router和fetch,基本上具備了開發web前端應用所需的主要工具,省下開發者自己進行配置安裝的工作。
開始使用dva
首先使用dva-cli快速創建dva應用。
npm install dva-cli -g
使用dva快速創建新的項目框架。
dva new my-new-project
dva會默認使用tnpm, cnpm依賴安裝,由於我現在的tnpm不能使用,只能把tnpm刪掉。
問題:如何配置dva的默認npm源
項目結構
dva-cli會創建如下的初始化項目結構
.
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .roadhogrc.mock.js
├── .webpackrc // webpack 配置
├── mock
│ └── .gitkeep
├── package.json
├── public
│ └── index.html
└── src
├── assets
│ └── yay.jpg
├── components
│ └── Example.js
├── index.css
├── index.js
├── models
│ └── example.js // 數據(狀態)管理中心
├── router.js // 路由
├── routes
│ ├── IndexPage.css
│ └── IndexPage.js // 路由中對應的頁面入口
├── services
│ └── example.js
└── utils
└── request.js
接下來根據我的個人喜好對項目進行微調 * 加入pages目錄,區別於components,前者用於放置頁面的個性化組件,後者用於放置複用性強的公共組件。然而這種組織一定程度上使得routes的存在沒有太大的意義。
在dva中使用redux
如果決定使用redux的話,需要在src/index.js里加入要用的model
// 3. Model
app.model(require('./models/example1').default);
app.model(require('./models/example2').default);
在dva中使用redux-router
在router.js中將頁面入口組件引入並配置相關的路由
import IndexPage from './routes/IndexPage';
import ManagerPage from './routes/ManagerPage';
import EntryPage from './routes/EntryPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/user" exact component={IndexPage} />
<Route path="/manager" exact component={ManagerPage} />
<Route path="/entry" exact component={EntryPage} />
</Switch>
</Router>
);
}
在model中使用redux-router
import { routerRedux } from 'dva/router';
put(routerRedux.push('/manager'))
routerRedux.push('/manager')
在組件中使用redux-router
import { routerRedux } from 'dva/router';
dispatch(routerRedux.push('/manager'))
接下來就可以愉快的開發了。
dva中的redux
首先,在使用dva開發時,可以將store拆分爲多個model,每個model對應一個namespace,對於複雜的應用,這種組織方式更貼近邏輯,使得問題更爲清晰。對比我之前在vue中將store拆爲state, getters, mutations, actions要更科學一些。後者的查分方式可以成爲“橫向拆分”, 而前者的拆分方式則是一種“縱向拆分”。 這種縱向拆分使得每一個拆分出來的子集都是一個子服務的對應。該自己可以包含自己的state,reducers和effects,可以認爲是一種前端微服務化的思想。
dva對store的構成與vuex相似,都將同步reducers和異步reducers拆分開來,以更好的管理監控異步操作。dva在異步處理上使用了redux-saga,藉助Genarator來實現異步操作。
問題:爲什麼不使用async呢?是歷史問題,還是二者在細節層面存在差別。
generator函數的使用
關於Generator Generator使用*標識函數爲Generator函數 使用yield阻斷異步操作,等到yield後的表達式完成計算得到返回值時,再繼續執行下面的代碼。 關於是否要對同步操作使用yield,我認爲沒有嚴格的限制。但如果其中存在你所關心的中間狀態變量,可以加上yield。這樣就可以在調用next()方法時在返回對象的value屬性中查詢到。
這點與async不同,async返回的是promise,而generator返回的則是狀態對象
redux-saga中異步effects的函數有三個參數
*postFile({ payload }, {call, put}) {
// handle both create and update
let res = yield call(fetch, '//api.center/postFile', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
let result = yield res.json()
if (result.success) {
yield put({ type: 'getFileList' })
}
},
{ payload } = action, 調用dispatch時所傳遞的參數
{call, put}call負責調用異步操作,put則調用其他reducers來完成對state的修改操作
這與vuex不同,vuex中的action可以直接修改state(也可以調用mutations),但直接修改會導致action的邏輯過於複雜,失去了分段調試監控的優勢。
call(異步操作函數,參數1,參數2,…)return 異步函數的返回值(promise或值)
put({type, payload}) 與dispatch參數相同,但type中不需要定義命名空間
reducers的設計
在dva給出的實例代碼中,有一個reducers的定義十分有趣
save(state, action) {
return { ...state, ...action.payload };
}
這種實現是對所有reducers的本質抽象,即修改state。在實踐簡單應用的開發時,基本上所有的reducers都可以只用這一個save來實現。那是不是無需編寫其他的reducers了呢? 我認爲不是的,對於如下情況,仍需對每一種情況編寫對應的reducers 修改的狀態(state)過多,導致難以通過修改值直接判斷reducer所代表的邏輯是什麼(在哪裏調用,爲什麼調用)。這種情況下建議新定義reducer,對一個state的操作集進行封裝,以便在調試時清楚被調用的reducer對應的業務邏輯是什麼。 需要根據參數進行預計算,最終得到state。這時,建議將一些重要的邏輯運算遷移到reducer中,以方便管理或複用。
開放的webpack配置
.webpackrc使得可以根據個人需求對react進行配置而無需進行eject操作。 (也可以將.webpackrc改爲.webpackrc.json從而使用json格式的配置) 如,我們可以配置babel插件來直接引入如antd或next之類的框架
{
"extraBabelPlugins": [
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": true
}]
]
}
如果不想使用css modules功能 (比如next的import配置失敗,只能手動導入css),還可以加入
“disableCSSModules”: true
不過,建議體驗一下css modules的功能
import styles from './style.css';
Hello React!
參考資料 Dva官網:[https://dvajs.com/guide/](https://dvajs.com/guide/)ES6 Generator:http://es6.ruanyifeng.com/#docs/generator
dva.js入門 https://www.jianshu.com/p/c7b3b9c98d04
antd配置:https://ant.design/docs/react/introduce-cn react eject:https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-eject
redux saga:https://redux-saga-in-chinese.js.org/