用 umi 搭建的 react 項目中,看 package.json 文件,我們可以看到:
"dependencies": { "dva": "^2.6.0-beta.6", "antd": "^3.19.5", "react": "^16.8.6", "react-dom": "^16.8.6" },
其實 umi 中就使用了 dva 。
先看一下目錄結構,我會標注出需要用到的文件:
├── dist/ // 默認的 build 輸出目錄 ├── mock/ // mock 文件所在目錄,基於 express ├── config/ ├── config.js // umi 配置,同 .umirc.js,二選一 └── src/ // 源碼目錄,可選 ├── layouts/index.js // 全局布局 ├── models // 全局的數據倉庫 類似於 redux ├── pages // 頁面目錄,里面的文件即路由 ├── myPage // 我創建的第一個文件夾 ├── index.js // 入口文件 ├── index.less └── model.js // 頁面級別的數據倉庫,相當於 頁面級別的 redux ├── .umi // dev 臨時目錄,需添加到 .gitignore ├── .umi-production // build 臨時目錄,會自動刪除 ├── document.ejs // HTML 模板 ├── 404.js // 404 頁面 ├── page1.js // 頁面 1,任意命名,導出 react 組件 ├── page1.test.js // 用例文件,umi test 會匹配所有 .test.js 和 .e2e.js 結尾的文件 └── page2.js // 頁面 2,任意命名 ├── services └── api.js // *放接口 ├── utils ├── config.js // *配置路徑 └── request.js // *封裝 fetch 方法 ├── global.css // 約定的全局樣式文件,自動引入,也可以用 global.less ├── global.js // 可以在這里加入 polyfill ├── app.js // 運行時配置文件 ├── .umirc.js // umi 配置,同 config/config.js,二選一 ├── .env // 環境變量 ├── .gitignore // 避免將不必要的代碼提交到 git 倉庫中 └── package.json
如果你做的是一個新項目,請不要跳過第一步。
一、fetch 請求簡單的封裝
1、在 utils 文件夾下創建 config.js 文件,utils/config.js
,對fetch請求路徑進行配置
//config.js文件 const config = { apiUrl: process.env.NODE_ENV === 'development' ? ' http://127.0.0.1:7001' : 'https://www.baidu.com', apiPrefix: ' http://127.0.0.1:7001', proxy: true //是否開啟mock代理 }; export default config;
2、簡單封裝 fetch 請求,utils文件夾下的 request.js 文件
//request.js文件 import fetch from 'dva/fetch'; import config from './config'; function parseJSON(response) { return response.json(); } function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } const assyParams = (obj) => { let str = '' for (let key in obj) { const value = typeof obj[key] !== 'string' ? JSON.stringify(obj[key]) : obj[key] str += '&' + key + '=' + value } return str.substr(1) } /** * 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 function request(obj) { let url = ''; let options = { method: obj.method, headers: { 'Content-Type': 'application/json; charset=utf-8', }, credentials: 'include' //是否攜帶cookie,默認為omit不攜帶; same-origi同源攜帶; include同源跨域都攜帶 }; if (obj.method === 'GET' || obj.method === 'get') { url = (config.proxy ? obj.url : config.apiUrl + obj.url) + '?' + assyParams(obj.data); } if (obj.method === 'POST' || obj.method === 'post') { url = config.proxy ? obj.url : config.apiUrl + obj.url; options.body = JSON.stringify(obj.data); } return fetch(url, options) .then(checkStatus) .then(parseJSON) .then(data => ({ data })) .catch(err => ({ err })) }
二、寫 api
1、service 文件下創建 api.js
import request from '../utils/request'; export function getSomeData(params) { return request({ method: "GET", url: `/appservice/common/v1/getSomeData`, data: JSON.stringify(params), }) }
三、mock 相關配置
1、在 mock 文件夾下新建 someData.js 文件 (文件名看工作需要修改)
const responseData = { status: 'ok', code: 200, data: "這是數據" } export default { // 支持值為 Object 和 Array 'GET /appservice/common/v1/getSomeData': responseData, // GET POST 可省略 比如: '/api/users/1': { id: 1 }, }
四、編寫 src/pages/myPage/model.js
/* export default { namespace: '', // 表示在全局 state 上的 key state: {}, // 狀態數據 reducers: {}, // 管理同步方法,必須是純函數 effects: {}, // 管理異步操作,采用了 generator 的相關概念 subscriptions: {}, // 訂閱數據源 }; call: 執行異步函數 put: 發出一個 Action,類似於 dispatch select: 返回 model 中的 state */ import { getSomeData, } from '../../services/api'; function initState() { return { modelNum: 0, text: "沒有返回" }; } export default { namespace: 'myPage', // 表示在全局 state 上的 key state: initState(), // 狀態數據 effects: { // 管理異步操作,采用了 generator 的相關概念 *getSomeData({ payload }, { call, put, select }) { const res = yield call(getSomeData, payload); if (res.data.code === 200) { // 拿到數據,可以選擇存到 model 中 yield put({ type: 'saveDefault', payload: { text: res.data.data }, }); } return res; }, }, reducers: { // 管理同步方法,必須是純函數 saveDefault(state, action) { return { ...state, ...action.payload, }; }, resetState() { // 重置 state return initState(); }, }, };
五、調用api發送請求
1、例如在 src/pages/myPage/index.js 頁面發送請求,並在頁面中顯示請求到的數據
2、如果在第4步中,選擇將接口返回的數據存在 model 中,此頁面就可以直接從 model 中獲取數據,不需要存到 state 中import React, { Component } from "react";import { Button } from 'antd';import { connect } from 'dva';
import { connect } from 'react-redux';
class secondPage extends Component { constructor(props) { super(props); this.state = { data: null } } getSomeData = async () => { const { dispatch } = this.props; await dispatch({ type: 'myPage/getSomeData' }).then((res) => { if (res && res.data && res.data.code === 200) { this.setState({ // 這里選擇將數據存在 state 中,也可以從 model 中獲取 data: res.data.data }) } }) } render() { const { data } = this.state; return ( <div> <div> <span>異步請求的返回:{data || "--"}</span> <Button onClick={() => this.getSomeData()} >請求接口</Button> </div> </div> ) } } const mapStateToProps = (model) => { console.log(model) // 查看 props 中的數據,可以拿到存在 model 中的數據。
return model.text // return 出來的數據,會被添加到對應的 props 中 }; export default connect(mapStateToProps)(secondPage)
/*
拿到 dispatch 的幾種方式
import { useDispatch } from 'react-redux';
import { useDispatch } from 'dva';
export default function (props) {
const dispatch = useDispatch();
return (
<div>
</div>
);
}
*/
結尾:對於一個沒人教的菜鳥來說,摸索出這一套,真的相當費勁,斷斷續續的隔了好幾周,終於完成了。
此文章復制可用,如果對你也有用,請一定要讓我知道,萬分感謝。