react前端框架dva(三)

青衫無名發表於2018-06-01

API

輸出檔案

dva

預設輸出檔案。

dva/router

預設輸出 react-router 介面, react-router-redux 的介面通過屬性 routerRedux 輸出。

比如:

import { Router, Route, routerRedux } from `dva/router`;

dva/fetch

非同步請求庫,輸出 isomorphic-fetch 的介面。不和 dva 強繫結,可以選擇任意的請求庫。

dva/saga

輸出 redux-saga 的介面,主要用於用例的編寫。(用例中需要用到 effects)

dva/dynamic

解決元件動態載入問題的 util 方法。

比如:

import dynamic from `dva/dynamic`;

const UserPageComponent = dynamic({
  app,
  models: () => [
    import(`./models/users`),
  ],
  component: () => import(`./routes/UserPage`),
});

opts 包含:

  • app: dva 例項,載入 models 時需要
  • models: 返回 Promise 陣列的函式,Promise 返回 dva model
  • component:返回 Promise 的函式,Promise 返回 React Component

dva API

app = dva(opts)

建立應用,返回 dva 例項。(注:dva 支援多例項)

opts 包含:

  • history:指定給路由用的 history,預設是 hashHistory
  • initialState:指定初始資料,優先順序高於 model 中的 state,預設是 {}

如果要配置 history 為 browserHistory,可以這樣:

import createHistory from `history/createBrowserHistory`;
const app = dva({
  history: createHistory(),
});

另外,出於易用性的考慮,opts 裡也可以配所有的 hooks ,下面包含全部的可配屬性:

const app = dva({
  history,
  initialState,
  onError,
  onAction,
  onStateChange,
  onReducer,
  onEffect,
  onHmr,
  extraReducers,
  extraEnhancers,
});

app.use(hooks)

配置 hooks 或者註冊外掛。(外掛最終返回的是 hooks )

比如註冊 dva-loading 外掛的例子:

import createLoading from `dva-loading`;
...
app.use(createLoading(opts));

hooks 包含:

onError((err, dispatch) => {})

effect 執行錯誤或 subscription 通過 done 主動拋錯時觸發,可用於管理全域性出錯狀態。

注意:subscription 並沒有加 try...catch,所以有錯誤時需通過第二個引數 done 主動拋錯。例子:

app.model({
  subscriptions: {
    setup({ dispatch }, done) {
      done(e);
    },
  },
});

如果我們用 antd,那麼最簡單的全域性錯誤處理通常會這麼做:

import { message } from `antd`;
const app = dva({
  onError(e) {
    message.error(e.message, /* duration */3);
  },
});

onAction(fn | fn[])

在 action 被 dispatch 時觸發,用於註冊 redux 中介軟體。支援函式或函式陣列格式。

例如我們要通過 redux-logger 列印日誌:

import createLogger from `redux-logger`;
const app = dva({
  onAction: createLogger(opts),
});

onStateChange(fn)

state 改變時觸發,可用於同步 state 到 localStorage,伺服器端等。

onReducer(fn)

封裝 reducer 執行。比如藉助 redux-undo 實現 redo/undo :

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 };
    },
  },
});

詳見 examples/count-undo 。

onEffect(fn)

封裝 effect 執行。比如 dva-loading 基於此實現了自動處理 loading 狀態。

onHmr(fn)

熱替換相關,目前用於 babel-plugin-dva-hmr 。

extraReducers

指定額外的 reducer,比如 redux-form 需要指定額外的 form reducer:

import { reducer as formReducer } from `redux-form`
const app = dva({
  extraReducers: {
    form: formReducer,
  },
});

extraEnhancers

指定額外的 StoreEnhancer ,比如結合 redux-persist 的使用:

import { persistStore, autoRehydrate } from `redux-persist`;
const app = dva({
  extraEnhancers: [autoRehydrate()],
});
persistStore(app._store);

app.model(model)

註冊 model,詳見 #Model 部分。

app.unmodel(namespace)

取消 model 註冊,清理 reducers, effects 和 subscriptions。subscription 如果沒有返回 unlisten 函式,使用 app.unmodel 會給予警告️。

app.router(({ history, app }) => RouterConfig)

註冊路由表。

通常是這樣的:

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 />);

app.start(selector?)

啟動應用。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);

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]

詳見: https://github.com/dvajs/dva/blob/master/packages/dva-core/test/reducers-test.js

effects

以 key/value 格式定義 effect。用於處理非同步操作和業務邏輯,不直接修改 state。由 action 觸發,可以觸發 action,可以和伺服器互動,可以獲取全域性 state 的資料等等。

格式為 *(action, effects) => void 或 [*(action, effects) => void, { type }]

type 型別有:

  • takeEvery
  • takeLatest
  • throttle
  • watcher

詳見:https://github.com/dvajs/dva/blob/master/packages/dva-core/test/effects-test.js

subscriptions

以 key/value 格式定義 subscription。subscription 是訂閱,用於訂閱一個資料來源,然後根據需要 dispatch 相應的 action。在 app.start() 時被執行,資料來源可以是當前的時間、伺服器的 websocket 連線、keyboard 輸入、geolocation 變化、history 路由變化等等。

格式為 ({ dispatch, history }, done) => unlistenFunction

注意:如果要使用 app.unmodel(),subscription 必須返回 unlisten 方法,用於取消資料訂閱。

原文釋出時間:2018年03月22日

本文來源CSDN部落格如需轉載請緊急聯絡作者


相關文章