dva 首先是一個基於 redux 和 redux-saga 的數據流方案,而後爲了簡化開發體驗,dva 還額外內置了 react-router 和 fetch,因此也能夠理解爲一個輕量級的應用框架。
dva官網地址:https://dvajs.com/html
import dva from 'dva'; // 1. Initialize const app = dva(); // 2. Plugins app.use({}); // 3. Model app.model(require('./models/example').default); // 4. Router app.router(require('./router').default); // 5. Start app.start('#root');
dva僅有6個api,以下介紹:node
建立應用,返回dva實例react
options中包含:web
(1) history:默認爲hashHistory,若是要配置history爲browserHistory,則redux
import createHistory from 'history/createBrowserHistory'; const app = dva({ history: createHistory(), });
(2) initialState: 指定初始數據,優先級高於model中的state,默認爲{}react-native
(3) onError: 管理全局出錯狀態,以下:api
const app = dva({ onError(e){ console.log(e); } });
(4) onAction(fn | fn[]): 在action被dispatch時觸發,用於註冊redux中間件,支持函數格式或者函數數組格式,以下經過redux-logger答應日誌,如:數組
import createLogger from 'redux-logger'; const app = dva({ onAction: createLogger(opts), });
(5) onStateChange(fn): state改變時觸發,可用於同步state 到 localStorage,服務器端等服務器
(6) onReducer(fn): 封裝 reducer 執行。好比藉助 redux-undo 實現 redo/undo :babel
import undoable from 'redux-undo'; const app = dva({ onReducer: reducer => { return (state, action) => { const undoOpts = {}; const newState = undoable(reducer, undoOpts)(state, action); // 因爲 dva 同步了 routing 數據,因此須要把這部分還原 return { ...newState, routing: newState.present.routing }; }, }, });
(7) onEffect(fn): 封裝 effect 執行。好比 dva-loading 基於此實現了自動處理 loading 狀態。
(8) onHmr(fn): 熱替換相關,目前用於 babel-plugin-dva-hmr
(9) extraReducers: 指定額外的 reducer,好比 redux-form 須要指定額外的 form reducer
import { reducer as formReducer } from 'redux-form' const app = dva({ extraReducers: { form: formReducer, }, });
(10) extraEnhancers: 指定額外的 StoreEnhancer ,好比結合 redux-persist 的使用
import { persistStore, autoRehydrate } from 'redux-persist'; const app = dva({ extraEnhancers: [autoRehydrate()], }); persistStore(app._store);
配置 hooks 或者註冊插件。(插件最終返回的是 hooks )
好比註冊 dva-loading 插件的例子:
import createLoading from 'dva-loading'; ... app.use(createLoading(opts));
hooks 包含2中(3)到(10)
註冊model
model 是 dva 中最重要的概念,如下是典型的例子:
app.model({ namespace: 'todo', state: [], reducers: { add(state, { payload: todo }) { // 保存數據到 state return [...state, todo]; }, }, effects: { *save({ payload: todo }, { put, call }) { // 調用 saveTodoToServer,成功後觸發 `add` action 保存到 state yield call(saveTodoToServer, todo); yield put({ type: 'add', payload: todo }); }, }, subscriptions: { setup({ history, dispatch }) { // 監聽 history 變化,當進入 `/` 時觸發 `load` action return history.listen(({ pathname }) => { if (pathname === '/') { dispatch({ type: 'load' }); } }); }, }, });
model 包含 5 個屬性:
namespace: model 的命名空間,同時也是他在全局 state 上的屬性,只能用字符串,不支持經過 . 的方式建立多層命名空間。
state: 初始值,優先級低於傳給 dva() 的 opts.initialState,以下:
const app = dva({ initialState: { count: 1 }, }); app.model({ namespace: 'count', state: 0, });
此時,在 app.start() 後 state.count 爲 1
reducers: 以 key/value 格式定義 reducer。用於處理同步操做,惟一能夠修改 state 的地方。由 action 觸發,格式爲 (state, action) => newState 或 [(state, action) => newState, enhancer]
effects: 以 key/value 格式定義 effect。用於處理異步操做和業務邏輯,不直接修改 state。由 action 觸發,能夠觸發 action,能夠和服務器交互,能夠獲取全局 state 的數據等等。格式爲
*(action, effects) => void 或 [*(action, effects) => void, { type }]。
subscriptions: 以 key/value 格式定義 subscription。subscription 是訂閱,用於訂閱一個數據源,而後根據須要 dispatch 相應的 action。在 app.start() 時被執行,數據源能夠是當前的時間、服務器的 websocket 鏈接、keyboard 輸入、geolocation 變化、history 路由變化等等。格式爲 ({ dispatch, history }, done) => unlistenFunction。注意:若是要使用 app.unmodel(),subscription 必須返回 unlisten 方法,用於取消數據訂閱。
取消 model 註冊,清理 reducers, effects 和 subscriptions。subscription 若是沒有返回 unlisten 函數,使用 app.unmodel 會給予警告
註冊路由表。一般是這樣的:
import { Router, Route } from 'dva/router'; app.router(({ history }) => { return ( <Router history={history}> <Route path="/" component={App} /> <Router> ); });
推薦把路由信息抽成一個單獨的文件,這樣結合 babel-plugin-dva-hmr 可實現路由和組件的熱加載,好比:
app.router(require('./router'));
而有些場景可能不使用路由,好比多頁應用,因此也能夠傳入返回 JSX 元素的函數。好比:
app.router(() => <App />);
啓動應用。selector 可選,若是沒有 selector 參數,會返回一個返回 JSX 元素的函數。
app.start('#root');
那麼何時不加 selector?常見場景有測試、node 端、react-native 和 i18n 國際化支持。
好比經過 react-intl 支持國際化的例子:
import { IntlProvider } from 'react-intl'; ... const App = app.start(); ReactDOM.render(<IntlProvider><App /></IntlProvider>, htmlElement);