dva.js 上手

初始化

安裝 dva-cli 用于初始化項目:

npm install -g dva-cli
# 或
yarn global add dva-cli

創(chuàng)建項目目錄,并進(jìn)入該目錄:

mkdir your-project
cd your-project

初始化項目:

dva init

然后運(yùn)行 npm startyarn start 即可運(yùn)行項目。

目錄結(jié)構(gòu)

項目初始化以后,默認(rèn)的目錄結(jié)構(gòu)如下:

|- mock
|- node_modules
|- package.json
|- public
|- src
    |- asserts
    |- components
    |- models
    |- routes
    |- services
    |- utils
    |- router.js
    |- index.js
    |- index.css
|- .editorconfig
|- .eslintrc
|- .gitignore
|- .roadhogrc.mock.js
|- .webpackrc
  • mock 存放用于 mock 數(shù)據(jù)的文件;
  • public 一般用于存放靜態(tài)文件,打包時會被直接復(fù)制到輸出目錄(./dist);
  • src 文件夾用于存放項目源代碼;
    • asserts 用于存放靜態(tài)資源,打包時會經(jīng)過 webpack 處理;
    • components 用于存放 React 組件,一般是該項目公用的無狀態(tài)組件;
    • models 用于存放模型文件
    • routes 用于存放需要 connect model 的路由組件;
    • services 用于存放服務(wù)文件,一般是網(wǎng)絡(luò)請求等;
    • utils 工具類庫
    • router.js 路由文件
    • index.js 項目的入口文件
    • index.css 一般是共用的樣式
  • .editorconfig 編輯器配置文件
  • .eslintrc ESLint配置文件
  • .gitignore Git忽略文件
  • .roadhogrc.mock.js Mock配置文件
  • .webpackrc 自定義的webpack配置文件,JSON格式,如果需要 JS 格式,可修改為 .webpackrc.js

antd 按需引入

先安裝 antdbabel-plugin-import

npm install antd babel-plugin-import --save
# 或
yarn add antd babel-plugin-import

babel-plugin-import 也可以通過 -D 參數(shù)安裝到 devDependencies 中,它用于實現(xiàn)按需加載。

然后在 .webpackrc 中添加如下配置:

{
  "extraBabelPlugins": [
    ["import", {
      "libraryName": "antd",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}

現(xiàn)在就可以按需引入 antd 的組件了,如 import { Button } from 'antd',Button 組件的樣式文件也會自動幫你引入。

更多 .webpackrc 的配置請參考 roadhog 配置。

自定義 antd 主題

可以在 .webpackrc 中添加 theme 字段直接進(jìn)行主題自定義,但是如果自定義的變量太多,建議將其單獨提取取來方便管理。

建議在 ./src 目錄下新建名為 theme.js 的文件,然后在 .webpackrc 中引入,如下:

{
  "theme": "./src/theme.js"
}

theme.js 示例如下:

export default {
  "primary-color": "#000",
}

更多可自定義的 antd 變量請參考 default.less。

CSS Modules

使用 dva-cli 初始化的項目默認(rèn)已經(jīng)啟用了 CSS Modules,如果不想使用 CSS Modules,在 .webpackrc 中添加以下配置項即可禁用:

{
  "disableCSSModules": true
}

開發(fā)代理

如需開發(fā)過程中代理 API 接口,在 .webpackrc 中添加如下配置即可:

{
  "proxy": {
    "/api": {
      "target": "http://your-api-server",
      "changeOrigin": true
    }
  }
}

Mock

如需 mock 功能,在 .roadhogrc.mock.js 中添加配置即可,如:

export default {
  'GET /api/users': { users: [{ username: 'admin' }] },
}

如上配置,當(dāng)請求 /api/users 時會返回 JSON 格式的數(shù)據(jù)。

同時也支持自定義函數(shù),如下:

export default {
  'POST /api/users': (req, res) => { res.end('OK'); },
}

具體的 API 請參考 Express.js@4。

當(dāng) mock 數(shù)據(jù)太多時,可以拆分后放到 ./mock 文件夾中,然后在 .roadhogrc.mock.js 中引入。

HMR

HMR,即模塊熱替換,在修改代碼后不需要刷新整個頁面,方便開發(fā)時的調(diào)試??梢栽?.webpackrc 中添加如下配置以使用 HMR:

{
  "env": {
    "development": {
      "extraBabelPlugins": [
        "dva-hmr"
      ]
    }
  }
}

如果無效,請嘗試更新一下 babel-plugin-dva-hmr。

env 字段是針對特定環(huán)境進(jìn)行配置,因為 HMR 只在開發(fā)環(huán)境下使用,所以將配置添加到 development 字段即可,運(yùn)行 npm run build 時的環(huán)境變量為 production。

組件動態(tài)加載

dva 內(nèi)置了 dynamic 方法用于實現(xiàn)組件的動態(tài)加載,用法如下:

import dynamic from 'dva/dynamic';

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

實際使用時,可以對其進(jìn)行簡單的封裝,否則每個路由組件都這么寫一遍很麻煩。

dva-loading

dva-loading 是一個用于處理 loading 狀態(tài)的 dva 插件,基于 dva 的管理 effects 執(zhí)行的 hook 實現(xiàn),它會在 state 中添加一個 loading 字段(該字段可自定義),自動幫你處理網(wǎng)絡(luò)請求的狀態(tài),不需要自己再去寫 showLoadinghideLoading 方法。

./src/index.js 中引入使用即可:

import createLoading from 'dva-loading';
const app = dva();
app.use(createLoading(opts));

opts 僅有一個 namespace 字段,默認(rèn)為 loading。

Model

Model 是 dva 最重要的部分,可以理解為 redux、react-redux、redux-saga 的封裝。

通常項目中一個模塊對應(yīng)一個 model,一個基本的 model 如下:

import { fetchUsers } from '../services/user';

export default {
  namespace: 'user',
  state: {
    list: [],
  },
  reducers: {
    save(state, action) {
      return {
        ...state,
        list: action.data,
      };
    },
  },
  effects: {
    *fetch(action, { put, call }) {
      const users = yield put(fetchUsers, action.data);
      yield put({ type: 'save', data: users });
    },
  },
  subscriptions: {
    setup({ dispatch, history }) {
      return history.listen(({ pathname }) => {
        if (pathname === '/user') {
          dispatch({ type: 'fetch' });
        }
      });
    },
  },
}

namespace 是該 model 的命名空間,同時也是全局 state 上的一個屬性,只能是字符串,不支持使用 . 創(chuàng)建多層命名空間。

state 是狀態(tài)的初始值。

reducer 類似于 redux 中的 reducer,它是一個純函數(shù),用于處理同步操作,是唯一可以修改 state 的地方,由 action 觸發(fā),它有 stateaction 兩個參數(shù)。

effects 用于處理異步操作,不能直接修改 state,由 action 觸發(fā),也可觸發(fā) action。它只能是 generator 函數(shù),并且有 actioneffects 兩個參數(shù)。第二個參數(shù) effects 包含 put、callselect 三個字段,put 用于觸發(fā) action,call 用于調(diào)用異步處理邏輯,select 用于從 state 中獲取數(shù)據(jù)。

subscriptions 用于訂閱某些數(shù)據(jù)源,并根據(jù)情況 dispatch 某些 action,格式為 ({ dispatch, history }, done) => unlistenFunction。

如上的一個 model,監(jiān)聽路由變化,當(dāng)進(jìn)入 /user 頁面時,執(zhí)行 effects 中的 fetch,以從服務(wù)端獲取用戶列表,然后 fetch 中觸發(fā) reducers 中的 save 將從服務(wù)端獲取到的數(shù)據(jù)保存到 state 中。

注意,在 model 中觸發(fā)這個 model 中的 action 時不需要寫命名空間,比如在 fetch 中觸發(fā) save 時是 { type: 'save' }。而在組件中觸發(fā) action 時就需要帶上命名空間了,比如在某個組件中觸發(fā) fetch 時,應(yīng)該是 { type: 'user/fetch' }。

app

./src/index.js 中可以看到如下代碼:

import dva from 'dva';
const app = dva();

app 就是 dva 實例,創(chuàng)建實例時 dva 方法可以傳入一些參數(shù),如下:

  • history
  • initialState
  • onError
  • onAction
  • onStateChange
  • onReducer
  • onEffect
  • onHmr
  • extraReducers
  • extraEnhancers

history 是給路由用的,默認(rèn)為 hashHistory,如果想要使用 browserHistory,需要安裝 history,然后在 ./src/index.js 引入使用:

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

initialState 是 state 的初始數(shù)據(jù),優(yōu)先級高于 model 中的 state,默認(rèn)為 {}。

其他以 on 開頭的均為鉤子函數(shù),更多請參考 dva API

connect

當(dāng)寫完 model 和組件后,需要將 model 和組件連接起來。dva 提供了 connect 方法,其實它就是 react-reduxconnect。用法如下:

import React from 'react';
import { connect } from 'dva';

const User = ({ dispatch, user }) => {
  return (
    <div></div>
  )
}

export default connect(({ user }) => {
  return user;
})(User);

connect 后的組件除了可以獲取到 dispatchstate,還可以獲取到 locationhistory。

錯誤處理

effectssubscriptions 拋出的錯誤都會經(jīng)過 onError 鉤子函數(shù),所以可以在 onError 中進(jìn)行全局錯誤處理。

const app = dva({
  onError(err, dispatch) {
    console.error(err);
  },
});

如果需要對某些 effects 進(jìn)行特殊的錯誤處理,可以使用 try catch。

異步請求

dva 集成了 isomorphic-fetch 用于處理異步請求,并且使用 dva-cli 初始化的項目中,已經(jīng)在 ./src/utils/request.js 中對 fetch 進(jìn)行了簡單的封裝,可以在這里根據(jù)服務(wù)端 API 的數(shù)據(jù)結(jié)構(gòu)進(jìn)行統(tǒng)一的錯誤處理。

當(dāng)然如果你不想用 fetch,完全可以引入自己喜歡的第三方庫,沒有任何影響,打包時也不會將 isomorphic-fetch 打包進(jìn)去。

參考鏈接

原文地址:https://pengtikui.cn/dva.js-get-started/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容