最近一個月來使用 dva 對公司存量項目進行重構,比較少時間寫文章了。隨着9月開學季節的到來,最近在使用的幾個開源項目都迎來了重大更新。首先就是 umi 終於迎來了 2.0 版本,具體介紹能夠查看 發佈 umi 2.0,可插拔的企業級 react 應用框架。隨之而來的是使用 umi@2 構建的 ant design pro 2.0 版本,具體介紹能夠查看漂亮的實力派 Ant Design Pro 2.0 正式發佈。今天終於有時間體驗了 umi@2 ,想起個人 dva 學習之路的第一篇學習記錄是使用 umi@1.X 構建的,恰好可使用 umi 2.0 從新構建一下。本文是 dva 做者 @sorrycc 的 umi-dva-user-dashboard 的 umi@2 版本實現,是 《umi + dva,完成用戶管理的 CURD 應用》 文章的 umi@2 版本改寫。代碼倉庫: umi2-dva-user-dashboard。演示地址:demo。css
詳見 umi官網#經過腳手架建立項目html
使用 yarn create
命令:react
$ mkdir umi2-dva-user-dashboard && cd umi2-dva-user-dashboard
$ yarn create umi
複製代碼
看到社區有小夥伴問到如何使用 npm 建立 umi 項目,首先使用 npm 安裝 create-umi :webpack
$ cnpm install -g create-umi
複製代碼
安裝好以後,進入項目,而後執行 create-umi
git
$ cd umi2-dva-user-dashboard
$ create-umi
複製代碼
注意官方推薦使用
yarn create
命令,由於能確保每次使用最新的腳手架。若是你和我同樣,公司是內網開發環境只有 npm 私服,能夠嘗試使用 npm 命令。github
完成上述操做後,進入 create-umi 交互式命令行,選擇功能,這裏選擇 antd、dva 和 hard sourceweb
肯定後,會根據你的選擇自動建立好目錄和文件: npm
而後手動安裝依賴:json
$ yarn
複製代碼
或api
$ cnpm install
複製代碼
安裝好依賴後啓動項目:
$ yarn start
複製代碼
或
$ npm start
複製代碼
若是順利,在瀏覽器打開 http://localhost:8000 可看到如下界面:
修改 .umirc.js
,加上 "proxy" 配置:
proxy: {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
},
複製代碼
注意,代理配置與 umi@1 的配置相同。
訪問 http://localhost:8000/api/users ,就能訪問到 jsonplaceholder.typicode.com/users 的數據。
umi 中文件即路由,因此咱們要新增路由,新建文件便可,這裏使用約定式路由。詳見 umijs.org/zh/guide/ro… 。
新建 src/pages/users.js
,內容以下:
export default () => {
return (
<div>
Users Page
</div>
)
}
複製代碼
而後訪問 http://localhost:8000/users ,你會看到 Users Page
的輸出:
這裏看到頁面多出了一個頭部,原來是 create-umi 腳手架生成的項目會默認生成一個全局 layout 文件 src/layouts/index.js
, umi@1.X 版本就具備這個特性,詳見官方文檔全局layout
注意剛纔創建的 src/pages/users.js
是簡單的頁面狀況,目前須要和 model 與 service 組織到一塊兒, 新建 src/pages/users/index.js
文件,將 src/pages/users.js
內容複製到 src/pages/users/index.js
文件中,而後刪除 src/pages/users.js
文件。
create-umi 腳手架沒有生成 request.js, 新建 src/utils/request.js
:
import fetch from 'dva/fetch';
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function request(url, options) {
const response = await fetch(url, options);
checkStatus(response);
const data = await response.json();
const ret = {
data,
headers: {},
};
if (response.headers.get('x-total-count')) {
ret.headers['x-total-count'] = response.headers.get('x-total-count');
}
return ret;
}
複製代碼
新增 service: src/pages/users/services/users.js
,注意 umi@2 添加了 webpack alias @,指向 src 目錄:
import request from '@/utils/request';
export function fetch({ page = 1 }) {
return request(`/api/users?_page=${page}&_limit=5`);
}
複製代碼
注意這裏的
page
參數默認爲1
,limit
參數設置爲5
新增 model: src/pages/users/models/users.js
,內容以下:
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
},
reducers: {
save(state, { payload: { data: list, total } }) {
return { ...state, list, total };
},
},
effects: {
*fetch({ payload: { page } }, { call, put }) {
const { data, headers } = yield call(usersService.fetch, { page });
yield put({ type: 'save', payload: { data, total: headers['x-total-count'] } });
},
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname, query }) => {
if (pathname === '/users') {
dispatch({ type: 'fetch', payload: query });
}
});
},
},
};
複製代碼
切換到瀏覽器(會自動刷新),應該沒任何變化,由於數據雖然好了,但並無視圖與之關聯。可是打開 Redux 開發者工具,應該能夠看到 users/fetch
和 users/save
的 action 以及相關的 state 。
咱們把組件存在 src/pages/users/components
裏,因此在這裏新建 Users.js
和 Users.css
。具體參考這個 Commit。
需留意兩件事:
src/constants.js
改完後,切換到瀏覽器,應該能看到帶分頁的用戶列表。
有幾點須要注意:
- Users.js 裏面將 model 和組件鏈接了起來,注意
const { list, total, page } = state.users;
裏面的users
爲model
裏面的namespace
名稱。- 咱們沒有手動註冊 model,umi 幫咱們進行了這一步操做, 詳見
src/pages/.umi/DvaContainer.js
文件,該文件會自動更新。相關規則詳見 umi官網#model註冊 一節。- 能夠直接使用 css module
添加頭部菜單組件,使得咱們能夠在首頁和用戶列表頁之間來回切換。
參考這個 Commit。
dva 有一個管理 effects 執行的 hook,並基於此封裝了 dva-loading 插件。經過這個插件,咱們能夠沒必要一遍遍地寫 showLoading 和 hideLoading,當發起請求時,插件會自動設置數據裏的 loading 狀態爲 true 或 false 。而後咱們在渲染 components 時綁定並根據這個數據進行渲染。
umi-plugin-dva 默認內置了 dva-loading 插件。
而後在 src/components/Users/Users.js
裏綁定 loading 數據:
+ loading: state.loading.models.users,
複製代碼
注意這裏的 users
爲 model
的 namespace
, 因此 dva-loading 的 loading 狀態是對於 model 總體的。
具體參考這個 Commit 。
刷新瀏覽器,你的用戶列表有 loading 了沒?
只改一個文件 src/pages/users/components/Users.js
就好。
處理分頁有兩個思路:
咱們用的是思路 2 的方式,好處是用戶能夠直接訪問到 page 2 或其餘頁面。
quan參考這個 Commit 。
通過前面的 8 步,應用的總體脈絡已經清晰,相信你們已經對總體流程也有了必定了解。
後面的功能調整基本均可以按照如下三步進行:
service
model
component 咱們如今開始增長用戶刪除功能。
service, 修改 src/pages/users/services/users.js
:
export function remove(id) {
return request(`/api/users/${id}`, {
method: 'DELETE',
});
}
複製代碼
src/pages/users/model.js
:*remove({ payload: id }, { call, put, select }) {
yield call(usersService.remove, id);
const page = yield select(state => state.users.page);
yield put({ type: 'fetch', payload: { page } });
},
複製代碼
src/pages/users/components/Users.js
,替換 deleteHandler
內容:dispatch({
type: 'users/remove',
payload: id,
});
複製代碼
注意因爲使用第三方api網站,數據並不會真的刪除,只是刪除api返回成功,選擇刪除後從新獲取數據,仍然是原來的數據。
處理用戶編輯和前面的同樣,遵循三步走:
src/pages/users/services/users.js
:export function patch(id, values) {
return request(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(values),
});
}
複製代碼
再是 model,修改 src/pages/users/model.js
:
*patch({ payload: { id, values } }, { call, put, select }) {
yield call(usersService.patch, id, values);
const page = yield select(state => state.users.page);
yield put({ type: 'fetch', payload: { page } });
},
複製代碼
最後是 component,詳見 Commit。
須要注意的一點是,咱們在這裏如何處理 Modal 的 visible 狀態,有幾種選擇:
另外,怎麼存也是個問題,能夠:
UserModal
的組件。完成後,切換到瀏覽器,應該就能對用戶進行編輯了。
相比用戶編輯,用戶建立更簡單些,由於能夠共用 UserModal
組件。和 Step 10 比較相似,就不累述了,詳見 Commit 。
到這裏,咱們已經完成了一個完整的 CURD 應用。若是感興趣,能夠進一步看下 dva 和 umi 的資料:
(完)
本文主要使用umi@2來完成 umi-dva-user-dashboard 項目。能夠看到在業務代碼上,和umi@1的寫法基本一致,遷移成本比較低。目前手頭的項目正在使用dva進行重構,暫時尚未使用 umi@2 的計劃,不過會持續關注 umi 的成長的。