本文會一步步引導大家如何建立一個 CURD 應用,包含查詢、編輯、刪除、建立,以及分頁處理,資料 mock,自動處理 loading 狀態等,基於 react, dva 和 antd 。
最終效果:
開始之前:
Step 1. 安裝 dva-cli 並建立應用
先安裝 dva-cli,並確保版本是 0.7 或以上。
1 2 3 |
$ npm i dva-cli -g $ dva -v 0.7.0 |
然後建立應用:
1 2 |
$ dva new user-dashboard $ cd user-dashboard |
Step 2. 配置 antd 和 babel-plugin-import
babel-plugin-import 用於按需引入 antd 的 JavaScript 和 CSS,這樣打包出來的檔案不至於太大。
1 2 |
$ npm i antd --save $ npm i babel-plugin-import --save-dev |
修改 .roadhogrc
,在 "extraBabelPlugins"
里加上:
1 |
["import", { "libraryName": "antd", "style": "css" }] |
Step 3. 配置代理,能通過 RESTFul 的方式訪問http://localhost:8000/api/users
修改 .roadhogrc
,加上 "proxy"
配置:
1 2 3 4 5 6 7 |
"proxy": { "/api": { "target": "http://jsonplaceholder.typicode.com/", "changeOrigin": true, "pathRewrite": { "^/api" : "" } } }, |
然後啟動應用:(這個命令一直開著,後面不需要重啟)
1 |
$ npm start |
瀏覽器會自動開啟,並開啟 http://localhost:8000 。
訪問 http://localhost:8000/api/users ,就能訪問到 http://jsonplaceholder.typicode.com/users 的資料。(由於 typicode.com 服務的穩定性,偶爾可能會失敗。不過沒關係,正好便於我們之後對於出錯的處理)
Step 4. 生成 users 路由
用 dva-cli 生成路由:
1 |
$ dva g route users |
然後訪問 http://localhost:8000/#/users 。
Step 5. 構造 users model 和 service
用 dva-cli 生成 Model :
1 |
$ dva g model users |
修改 src/models/users.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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 }); } }); }, }, }; |
新增 src/services/users.js
:
1 2 3 4 5 |
import request from '../utils/request'; export function fetch({ page = 1 }) { return request(`/api/users?_page=${page}&_limit=5`); } |
由於我們需要從 response headers 中獲取 total users 數量,所以需要改造下 src/utils/request.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
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; } |
切換到瀏覽器(會自動重新整理),應該沒任何變化,因為資料雖然好了,但並沒有檢視與之關聯。但是開啟 Redux 開發者工具,應該可以看到 users/fetch
和 users/save
的 action 以及相關的 state 。
Step 6. 新增介面,讓使用者列表展現出來
用 dva-cli 生成 component:
1 |
$ dva g component Users/Users |
然後修改生成出來的 src/components/Users/Users.js
和 src/components/Users/Users.css
,並在src/routes/Users.js
中引用他。具體參考這個 Commit。
需留意兩件事:
- 對 model 進行了微調,加入了 page 表示當前頁
- 由於 components 和 services 中都用到了 pageSize,所以提取到
src/constants.js
改完後,切換到瀏覽器,應該能看到帶分頁的使用者列表。
Step 7. 新增 layout
新增 layout 佈局,使得我們可以在首頁和使用者列表頁之間來回切換。
- 新增布局,
src/components/MainLayout/MainLayout.js
和 CSS 檔案 - 在
src/routes
資料夾下的檔案中引用這個佈局
參考這個 Commit。
注意:
- 頁頭的選單會隨著頁面切換變化,高亮顯示當前頁所在的選單項
Step 8. 通過 dva-loading 處理 loading 狀態
dva 有一個管理 effects 執行的 hook,並基於此封裝了 dva-loading 外掛。通過這個外掛,我們可以不必一遍遍地寫 showLoading 和 hideLoading,當發起請求時,外掛會自動設定資料裡的 loading 狀態為 true 或 false 。然後我們在渲染 components 時繫結並根據這個資料進行渲染。
先安裝 dva-loading :
1 |
$ npm i dva-loading --save |
修改 src/index.js
載入外掛,在合適的地方加入下面兩句:
1 2 |
+ import createLoading from 'dva-loading'; + app.use(createLoading()); |
然後在 src/components/Users/Users.js
裡繫結 loading 資料:
1 |
+ loading: state.loading.models.users, |
具體參考這個 Commit 。
切換到瀏覽器,你的使用者列表有 loading 了沒?
Step 9. 處理分頁
只改一個檔案 src/components/Users/Users.js
就好。
處理分頁有兩個思路:
- 發 action,請求新的分頁資料,儲存到 model,然後自動更新頁面
- 切換路由 (由於之前監聽了路由變化,所以後續的事情會自動處理)
我們用的是思路 2 的方式,好處是使用者可以直接訪問到 page 2 或其他頁面。
參考這個 Commit 。
Step 10. 處理使用者刪除
經過前面的 9 步,應用的整體脈絡已經清晰,相信大家已經對整體流程也有了一定了解。
後面的功能調整基本都可以按照以下三步進行:
- service
- model
- component
我們現在開始增加使用者刪除功能。
- service, 修改
src/services/users.js
:
12345export function remove(id) {return request(`/api/users/${id}`, {method: 'DELETE',});}
- model, 修改
src/models/users.js
:
12345*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 } });},
- component, 修改
src/components/Users/Users.js
,替換deleteHandler
內容:
1234dispatch({type: 'users/remove',payload: id,});
切換到瀏覽器,刪除功能應該已經生效。
Step 11. 處理使用者編輯
處理使用者編輯和前面的一樣,遵循三步走:
- service
- model
- component
先是 service,修改 src/services/users.js
:
1 2 3 4 5 6 |
export function patch(id, values) { return request(`/api/users/${id}`, { method: 'PATCH', body: JSON.stringify(values), }); } |
再是 model,修改 src/models/users.js
:
1 2 3 4 5 |
*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。
需要注意的一點是,我們在這裡如何處理 Model 的 visible 狀態,有幾種選擇:
- 存 dva 的 model state 裡
- 存 component state 裡
另外,怎麼存也是個問題,可以:
- 只有一個 visible,然後根據使用者點選的 user 填不同的表單資料
- 幾個 user 幾個 visible
此教程選的方案是 2-2,即存 component state,並且 visible 按 user 存。另外為了使用的簡便,封裝了一個UserModal
的元件。
完成後,切換到瀏覽器,應該就能對使用者進行編輯了。
Step 12. 處理使用者建立
相比使用者編輯,使用者建立更簡單些,因為可以共用 UserModal
元件。和 Step 11 比較類似,就不累述了,詳見 Commit 。
到這裡,我們已經完成了一個完整的 CURD 應用。但僅僅是完成,並不完善,比如:
- 如何處理錯誤,比如請求等
- 如何處理請求超時
- 如何根據路由動態載入 JS 和 CSS
- …
請期待下一篇。
(完)