步驟:css
建立應用,返回 dva 實例。(注:dva 支持多實例)。node
const app = dva({
history, // 指定給路由用的 history,默認是 hashHistory
initialState, // 指定初始數據,優先級高於 model 中的 state
onError, // effect 執行錯誤或 subscription 經過 done 主動拋錯時觸發,可用於管理全局出錯狀態。
onAction, // 在 action 被 dispatch 時觸發
onStateChange, // state 改變時觸發,可用於同步 state 到 localStorage,服務器端等
onReducer, // 封裝 reducer 執行。好比藉助 redux-undo 實現 redo/undo
onEffect, // 封裝 effect
onHmr, // 熱替換相關
extraReducers, // 指定額外的 reducer,好比 redux-form 須要指定額外的 form reducer
extraEnhancers, // 指定額外的 StoreEnhancer ,好比結合 redux-persist 的使用
});
複製代碼
這裏能夠對如下的hook進行option配置 這裏能夠將hashhistory轉化爲browserHistoryreact
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});
複製代碼
一樣能夠配置hooks以及註冊其餘插件webpack
import createLoading from 'dva-loading';
...
app.use(createLoading(opts));
複製代碼
在普通的react-redux+redux-saga的項目中,咱們首先會建4個文件夾,分別是actions,reducer,saga,組件,還有獲取請求數據的services文件夾,一樣在入口文件那要引入不少中間件、provider、connect等去將這幾個文件夾聯繫起來,在這裏的model如下就將這些集成在了一塊兒,大大減少了開發工做量。 web
namespace model 的命名空間,同時也是他在全局 state 上的屬性,只能用字符串,不支持經過 . 的方式建立多層命名空間。至關於這個model的key 在組件裏面,經過connect+這個key將想要引入的model加入數據庫
import { connect } from 'dva'
複製代碼
const app = dva({
initialState: { count: 1 },
});
app.model({
namespace: 'count',
state: 0,
});
複製代碼
此時爲1express
['setQuery']: [function*() {}, { type: 'takeEvery'}],
複製代碼
- takeEvery監聽action的每次變化執行(默認)
- takeLatest監聽action最近一次的變化
- take監聽一次action留着,後面執行動做
複製代碼
Effect 被稱爲反作用,在咱們的應用中,最多見的就是異步操做。它來自於函數編程的概念,之因此叫反作用是由於它使得咱們的函數變得不純,一樣的輸入不必定得到一樣的輸出。npm
dva 爲了控制反作用的操做,底層引入了redux-sagas作異步流程控制,因爲採用了generator的相關概念,因此將異步轉成同步寫法,從而將effects轉爲純函數。至於爲何咱們這麼糾結於 純函數,若是你想了解更多能夠閱讀Mostly adequate guide to FP,或者它的中文譯本JS函數式編程指南。編程
純函數的好處:將函數抽離出來,與業務不耦合json
更有利於單元測試
無反作用(side-effect),不會修改做用域外的值,使代碼好調試
執行順序不會對系統形成影響
剝離出業務邏輯,好複用
複製代碼
const { dispatch } = this.props;
dispatch({
type: 'app/updateState' ,
payload: {
opacityTop: 'none',//控制top的透明度
hiddenDivDisplay: 'none',//控制隱藏頭部的display
footerDisplay: 'none'//控制footer的display
}
});
複製代碼
subscriptions 以 key/value 格式定義 subscription。subscription 是訂閱,用於訂閱一個數據源,而後根據須要 dispatch 相應的 action。在 app.start() 時被執行,數據源能夠是當前的時間、服務器的 websocket 鏈接、keyboard 輸入、geolocation 變化、history 路由變化等等。
格式爲 ({ dispatch, history }, done) => unlistenFunction。
注意:若是要使用 app.unmodel(),subscription 必須返回 unlisten 方法,用於取消數據訂閱。
複製代碼
app.router(require('./router'));
app.router(() => <App />);
複製代碼
啓動應用。selector 可選,若是沒有 selector 參數,會返回一個返回 JSX 元素的函數。 selector爲根元素
app.start('#root');
複製代碼
roadhog server 支持 mock 功能,相似 dora-plugin-proxy,在 .roadhogrc.mock.js 中進行配置,支持基於 require 動態分析的實時刷新,支持 ES6 語法,以及友好的出錯提示。在配置文件進行一下(node語法)配置,就能夠經過簡單的fetch請求獲取到數據。
.roadhogrc.mock.js
export default {
// 支持值爲 Object 和 Array
'GET /api/users': { users: [1,2] },
// GET POST 可省略
'/api/users/1': { id: 1 },
// 支持自定義函數,API 參考 express@4
'POST /api/users/create': (req, res) => { res.end('OK'); },
// Forward 到另外一個服務器
'GET /assets/*': 'https://assets.online/',
// Forward 到另外一個服務器,並指定子路徑
// 請求 /someDir/0.0.50/index.css 會被代理到 https://g.alicdn.com/tb-page/taobao-home, 實際返回 https://g.alicdn.com/tb-page/taobao-home/0.0.50/index.css
'GET /someDir/(.*)': 'https://g.alicdn.com/tb-page/taobao-home',
};
複製代碼
若爲多接口應用,則在mock文件夾下利用mockjs進行數據模擬,再在配置文件裏,進行文件遍歷引入
mock->user.js
const qs = require('qs');
const mockjs = require('mockjs'); //導入mock.js的模塊
const Random = mockjs.Random; //導入mock.js的隨機數
// 數據持久化 保存在global的全局變量中
let tableListData = {};
if (!global.tableListData) {
const data = mockjs.mock({
'data|100': [{
'id|+1': 1,
'name': () => {
return Random.cname();
},
'mobile': /1(3[0-9]|4[57]|5[0-35-9]|7[01678]|8[0-9])\d{8}/,
}],
page: {
total: 100,
current: 1,
},
});
tableListData = data;
global.tableListData = tableListData;
} else {
tableListData = global.tableListData;
}
module.exports = {
//post請求 /api/users/ 是攔截的地址 方法內部接受 request response對象
'GET /users' (req, res) {
setTimeout(() => {
res.json({ //將請求json格式返回
success: true,
data,
page: '123',
});
}, 200);
},
.roadhogrc.mock.js
const mock = {}
require('fs').readdirSync(require('path').join(__dirname + '/mock')).forEach(function(file) {
Object.assign(mock, require('./mock/' + file))
})
module.exports = mock
複製代碼
格式爲 JSON,容許註釋,布爾類型的配置項默認值均爲 false,支持經過 webpack.config.js 以編碼的方式進行配置,但不推薦,由於 roadhog 自己的 major 或 minor 升級可能會引發兼容問題。
"extraBabelPlugins": [
"transform-runtime",
"dva-hmr",
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css" }]
]
複製代碼
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
}
複製代碼
multipage 配置是否多頁應用。多頁應用會自動提取公共部分爲 common.js 和 common.css 。
define 配置 webpack 的 DefinePlugin 插件,define 的值會自動作 JSON.stringify 處理。
env 針對特定的環境進行配置。server 的環境變量是 development,build 的環境變量是 production。防止生產環境冗餘。
"extraBabelPlugins": ["transform-runtime"],
"env": {
"development": {
"extraBabelPlugins": ["dva-hmr"]
}
}
複製代碼
"theme": {
"@primary-color": "#1DA57A"
}
/
"theme": "./node_modules/abc/theme-config.js"
複製代碼
在router.js中使用,動態加載model和component app: dva 實例,加載 models 時須要 models: 返回 Promise 數組的函數,Promise 返回 dva model component:返回 Promise 的函數,Promise 返回 React Component
在roadhog中引入他們本身封裝的af-webpack,這裏面用css-loader以及加上.webpackrc的配置對css進行模塊化,將css結果js的一層封裝,給classname後面加上隨機的hash,使得classname不會衝突,若要全局的就加上:global便可
若是當前應用中加載了不止一個model,在其中一個的effect裏面作select操做,是能夠獲取另一箇中的state的:
*foo(action, { select }) {
const { a, b } = yield select();
}
複製代碼
function createModel(options) {
const { namespace, param } = options;
return {
namespace: `demo${namespace}`,
states: {},
reducers: {},
effects: {
*foo() {
// 這裏能夠根據param來肯定下面這個call的參數
yield call()
}
}
};
}
const modelA = createModel({ namespace: 'A', param: { type: 'A' } });
const modelB = createModel({ namespace: 'A', param: { type: 'B' } });
複製代碼
const [result1, result2] = yield all([
call(service1, param1),
call(service2, param2)
])
複製代碼
const { data, timeout } = yield race({
data: call(service, 'some data'),
timeout: call(delay, 1000)
});
if (data)
put({type: 'DATA_RECEIVED', data});
else
put({type: 'TIMEOUT_ERROR'});
複製代碼
若是這裏是要在組件裏面作某些事情,怎麼辦? 將resolve傳給model
new Promise((resolve, reject) => {
dispatch({ type: 'reusable/addLog', payload: { data: 9527, resolve, reject } });
})
.then((data) => {
console.log(`after a long time, ${data} returns`);
});
複製代碼
在model進行跨model通訊
try {
const result = yield call(service1);
yield put({ type: 'service1Success', payload: result });
resolve(result);
}
catch (error) {
yield put({ type: 'service1Fail', error });
reject(ex);
}
複製代碼
roadhog主要是依賴於他們本身封裝的af-webpack
在getUserConfig的文件夾下,直接經過json內容去獲取配置
在config下進行每一項的校驗 在index,js中調用watch.js的方法去監聽咱們的配置文件,而監聽文件夾用的是chokidar這個包