# Dva 入門
概念:dva是體驗技術部開發的react應用框架,將react-router/redux/redux-saga(異步操做)三個工具庫包裝在一塊兒,簡化API,讓react應用更加方便、快捷。
dva是framwork,不是library,相似emberjs,使用依賴庫自己的語法。簡化開發,至關於一個大的語法糖。
數據流圖:start-(connect)->view-(dispatch)->action
state:經過建立一個state對象,保存整個應用狀態,
action:是一個對象,用來描述事件;
connect方法:一個函數,綁定state到view;返回的也是react組件,成爲容器組件,是原始的UI組件的容器在外面包裹一層state。接收兩個參數mapStateToProps函數,mapStateToProps函數返回一個對象,用於創建state到props的映射關係。
dispatch方法:一個函數,發送action到state;被connect的component會自動在props中擁有dispatch方法。
Provider和connect()很是優雅的將全局store提供給了每個組件。讓每個組件能夠很是方便的去使用全局的數據。
roadhog(如同webpack)
1. Model對象的屬性
namespace:當前Model的名稱。整個應用的state,由多個曉得Model的state以namespace爲key合成。
state:該Model當前的狀態,數據保存在這裏,直接決定了試圖層的輸出。
reducer:Action處理器,處理同步動做,用來算出最新的state;
effects:Action處理器,處理異步動做;基於redux-saga實現,典型的就是I/O操做、數據庫讀寫。不能直接修改state,由action觸發。
Generator函數:effect是一個Generator函數,內部使用yield關鍵字,表示每一步的操做(不論是異步仍是同步)
call和put:dva提供多個effect函數內部的處理函數,經常使用的是call和put. call是執行異步操做,put發出一個action,相似dispatch.
```
{
namespace: 'count',
state: 0,
reducers: {
add(state) { return state + 1 },
},
effects: {
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'add' });
},
},
}
```
2. dva安裝
1、開始新項目的準備工做:
一、安裝dva-cli並確保版本在0.9.1或以上版本:
npm install dva-cli -g
檢測版本號: dva -v
二、建立項目名爲‘wmz’的新項目:
dva new wmz
三、安裝antd 和 babel-plugin-import ,babel-plugin-import 是用來按需加載 antd 的腳本和樣式的,須要用tnpm 去下載ant插件。
下載tnpm:有的不須要也能夠
npm install tnpm -g --registry=http://registry.npm.alibaba-inc.com
tnpm install
下載antd:
npm install antd babel-plugin-import --save
注意:下載完插件後,須要在.webpackrc文件中添加如下內容,才能使 babel-plugin-import 插件生效。
"extraBabelPlugins": [
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
]
四、準備工做作完以後,開始啓動項目:
npm start
2、新項目實戰
一、建立路由,路由能夠想象成是組成應用的不一樣頁面。文件的位置:routes文件夾裏面。
二、添加路由信息到路由表,在router.js中進行編輯,先引入路由文件,再設置文件路徑.
此時,就已經能夠在http:127.0.0.1:8000,看到頁面信息了;
三、編寫UI Component,文件位置:component文件夾裏;場景:在多個頁面分享UI元素(或者在一個頁面使用屢次),在dva裏能夠把這部分抽成component。
四、定義model,dva經過model的概念把一個領域的模型管理起來,包含同步更新state的reducer,處理異步邏輯的effects,訂閱數據源的subscriptions。文件的位置在models文件夾裏。
在這個model裏:
namespace:表示在全局state上的key;
state: 是初始值;
reducer: 等同於redux裏的reducer,接收action,同步更新state;
最後記得必定要在index.js文件裏載入model:
app.model(require(‘./models/文件名’).default);
五、connect方法:把model和component串聯起來。文件位置:routers文件夾裏。
六、最後須要在index.js 中添加初始數據讓項目run起來;
const app = dva();
3、項目模塊安排
項目的總體設計模式:
1. 先設計model
namespace 是 model state 在全局 state 所用的 key,state 是默認數據。
異步處理:dva 經過對 model 增長 effects 屬性來處理 side effect(異步任務),call,put;
鍵盤訂閱事件:subscription和model綁定,用於訂閱一個數據源,而後根據dispatch須要的action,數據源能夠是當前時間、服務器的websocket、keyword輸入、geolocation變化、history路由變化等。
2. 再設計component
經過 props 傳入兩個值,count 和 dispatch,count 對應 model 上的 state,在後面 connect 的時候綁定,dispatch用於分發 action
更新state是經過reducer處理的,也是惟一能夠更新state的地方。
3. 最後連接model和component
router:寫組件,接收dispatch和products兩個參數;
小案例: import React from 'react'; import {connect} from 'dva'; import ProductList from '../components/ProductList'; const Products = ({dispatch,products}) => { function handleDelete(id){ dispatch({//調用dva中的方法 type:'products/delete', payload: id, }) }; return( <div> <h2>list of products</h2> {/*給組件綁定兩個時間,刪除事件和同步處理數據dva */} <ProductList onDelete={handleDelete} products={products}/> </div> ) } export default connect(({ products})=>({ products, }))(Products); ``` models:定義model,對state進行管理,處理組件中的同步的方法; ``` //在組件中調用 export default { namespace : 'products', state : [], reducers : { 'delete'(state,{payload:id}){//接收兩個參數state和delete接收的參數 return state.filter(item =>item.id !== id);//filter返回true或者false,是否保留該數據。 } } } ``` components:一個對象,接收父組件綁定在子組件上的方法和models; ``` import React from 'react'; import PropTypes from 'prop-types';//設置數據類型 import {Table,Popconfirm,Button} from 'antd'; const ProductList = ({onDelete,products}) =>{//接收方法 const columns = [{ title:'Name', dataIndex:'name' },{ title:'Actions', render:(text,record)=>{ //編輯單元格 return( //氣泡框 onConfirm確認的 <Popconfirm title="刪除?" onConfirm={()=>onDelete(record.id)}> <Button>Delete</Button> </Popconfirm> ); } }]; return( <Table //表格 dataSource = {products} columns = {columns} /> ) } export default ProductList;
## Dva源碼解析
1. roadhog 起的是 webpack 自動打包和熱更替的做用。
2. dva是一個杉樹,返回一個app對象;目前 dva 的源碼核心部分包含兩部分,dva 和 dva-core。前者用高階組件 React-redux 實現了 view 層,後者是用 redux-saga 解決了 model 層。
3. src/index.js
在這裏,dva 作了三件比較重要的事情:
使用 call 給 dva-core 實例化的 app(這個時候還只有數據層) 的 start 方法增長了一些新功能(或者說,經過代理模式給 model 層增長了 view 層)。
使用 react-redux 完成了 react 到 redux 的鏈接。
添加了 redux 的中間件 react-redux-router,強化了 history 對象的功能。
4. provider本質上是一個高階組件,也是一種代理模式,接收 redux 生成的 store 作參數後,經過上下文 context 將 store 傳遞進被代理組件。在保留原組件的功能不變的同時,增長了 store 的 dispatch 等方法。
5. connect 也是一個代理模式實現的高階組件,爲被代理的組件實現了從 context 中得到 store 的方法。
6. initialState 是 state 的初始數據,優先級高於 model 中的 state,默認爲 {}。
7. createLoading() 加載狀態,在app.use(CreateLoading());根據state.loading.global指示全局loading狀態。若是在組件中直接使用this.props.locading
## Dva
1. 解決的主要問題
簡化了react-redux的部署文件的複雜度,可以快速上手;
2. 主要接口:
(1)後端接口:
配置路由,在點擊按鈕後,在componentWillMount中經過this.props.dispatch派發,在model中執行effects、reducer等操做;在servers的文件夾下發送網絡請求須要經過request.js文件,後臺接收:
export function patch(id, values) {
return request(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(values),
});
}
(2)mock數據:在.roadhogrc.mock.js中添加配置
export default {
'GET /api/users': { users: [{ username: 'admin' }] },
}
export default {
'POST /api/users': (req, res) => { res.end('OK'); },
}
在roadhogrc中進行配置proxy;如果get請求,則能夠直接在url後面進行拼接;
3. 主要執行路徑
用戶操做後或者路由跳轉->dispatch(action),同步直接經過reducer更改state,異步先觸發effects,而後經過reducer最終改變state.
4. 可配置及修改的類、函數和文件
(1)路由:經過瀏覽器的History API能夠監聽瀏覽器url的變化,從而控制路由相關操做
(2)在入口文件index.js中 可配置plugin/model/router
state:model的狀態數據,對象形式;
action:是一個javascript對象,改變state的惟一途徑。必須帶有type屬性,須要注意的是 dispatch 是在組件 connect Models之後,經過 props 傳入的;
dispatch:payload是須要傳遞的信息;經過props傳入的dispatch函數;
reducers:{ save(state,{payload: }){ return { }}}返回一個新值;
effects:{fetch({payload},{call,put})} 不能直接修改state,由action觸發。call是執行異步操做, put發出一個action,相似dispatch.
import * as 自定義名 from '../services/service';
effects: {
*供組件調用的方法名({ payload: { 參數 }}, { call, put }) {
const result = yield call(自定義名.service中的方法名, 參數);//若是使用 {參數} ,則是一個對象
//把請求的數據保存起來
//數據更新會帶動頁面從新渲染
yield put({
type: 'save', //reducers中的方法名
payload:{
data: result.data //網絡返回的要保留的數據
}
})
}
},
監聽路由變化,當進入 /user 頁面時,執行 effects 中的 fetch,以從服務端獲取用戶列表,而後 fetch 中觸發 reducers 中的 save 將從服務端獲取到的數據保存到 state 中。
effects: { *fetch(action, { put, call }) { const users = yield put(fetchUsers, action.data); yield put({ type: 'save', data: users }); }, }, *create({ payload: values }, { call, put }) { yield call(usersService.create, values); yield put({ type: 'reload' }); }, *reload(action, { put, select }) { const page = yield select(state => state.users.page); yield put({ type: 'fetch', payload: { page } }); },
Subscription :語義是訂閱,用於訂閱一個數據源,而後根據條件 dispatch 須要的 action。數據源能夠是 當前的時間、服務器的 websocket 鏈接、keyboard 輸入、geolocation 變化、history 路由變化等等。
(3)在.webpackrc中經過添加字段來進行自定義antd主題;
CSS Modules配置,