Umi+Dva入门
1_Dva、Umi初识
1.1_Dva在前端的位置
- 只有React可以做前端,但是数据的共享传输会是一个大问题
- 后来React+Redux可以让数据存在一个总的仓库中,并优化了数据的传输问题,但是对于异步的数据传输并没有解决
- 后来React+Redux+Redux-saga解决了异步的数据传输问题
- 后来React+Redux+Redux-saga+React-router
- 但是这么多技术太繁琐了,所以Dva诞生了,只需要React+Dva即可代替4
1.2_Umi作用
作用1:用于整合
作用2:用于自动配置路由
1.3_建立一个Umi项目
脚手架
先找个地方建个空目录。
$ mkdir myapp && cd myapp
通过官方工具创建项目,
$ yarn create @umijs/umi-app
Copy: .editorconfigWrite: .gitignoreCopy: .prettierignoreCopy: .prettierrcWrite: .umirc.tsCopy: mock/.gitkeepWrite: package.jsonCopy: README.mdCopy: src/pages/index.lessCopy: src/pages/index.tsxCopy: tsconfig.jsonCopy: typings.d.ts
安装依赖
$ yarn
yarn install v1.21.1[1/4] 🔍 Resolving packages...success Already up-to-date.✨ Done in 0.71s.
启动项目
$ yarn start
Starting the development server...
✔ Webpack Compiled successfully in 17.84s
DONE Compiled successfully in 17842ms 8:06:31 PM
App running at: - Local: http://localhost:8000 (copied to clipboard) - Network: http://192.168.12.34:8000
1.4_整理项目结构
2_Dva详述
2.1_Model模块
model就是存储数据、中转数据的仓库。
写法:
解释:action = {type,payload},type一般没用
effects = {put,call}
import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
const TryAModel = {
namespace: 'tryamodel',
state: {
name: '',
},
/*
effects 异步操作,经常用来和后端交互
*/
effects: {
*function_name(action:any, effects :any) {},
},
/*
同步操作:经常用于仓库给各组件传值
*/
reducers: {
function_name(state,action) {}
// 启用 immer 之后
// save(state, action) {//state是旧值
// state.name = action.payload;
// newState = state
// return newState
// },
},
subscriptions: {
//如果路径名是'/'则调用function_name函数
setup({ dispatch, history }:any) {
return history.listen(({ pathname }:any) => {
if (pathname === '/') {
dispatch({
type: 'function_name',
});
}
});
},
},
};
export default TryAModel;
3_流程模板
3.1_组件模板
import connect from "umi";
const SecondExample = (props:any) =>{
console.log(props.Model_SecondExampleModel)
const clickTry = (props:any) =>{
if (props.dispatch){
props.dispatch({
//路径:model的namespace+effects函数名
type: 'Model_SecondExampleModel/getListFromServicesEffects',
payload: {
callback: (value: any) => {
console.log(value);
},
},
})
}
console.log(props.Model_SecondExampleModel)
}
return (
<div>
<a onClick={()=>{clickTry(props)}}>SecondExample</a>
</div>
);
}
//连接的参数名必须是model的namespace
export default connect(({ Model_SecondExampleModel }: any) => ({
Model_SecondExampleModel,
}))(SecondExample);
3.2_Model(注意文件夹名是models且建在src下)
有两种方式
第一种会将数据存到仓库再返回
第二种会将数据直接返回
FirstExampleModel
import {Reducer, Effect, Subscription} from "umi";
/*
-----------------------------------------------------------------------------------------------------------------
本model用来演示正规的流程,即
①组件连接仓库;
②组件通过connect连接后给组件自动传的参数dispatch访问仓库中的异步函数effects;
③effects获取到数据后调用同步函数reducers存储数据到仓库state中
④组件自动更新参数并重新渲染
-----------------------------------------------------------------------------------------------------------------
*/
/*
-----------------------------------------------------------------------------------------------------------------
ts规范:对变量进行声明
*/
//对state声明,state用于仓库存储数据,也就是说state就是仓库
export type StateType = {
list?: any[];
};
//对model声明
export type ExampleModelType = {
namespace: string;
state: StateType;
effects: {
getListFromServicesEffects: Effect;
};
reducers: {
getListReducers: Reducer<StateType>;
};
subscriptions: {
// setup: Subscription;
}
};
/*
ts规范:对变量进行声明
-----------------------------------------------------------------------------------------------------------------
*/
/*
-----------------------------------------------------------------------------------------------------------------
model结构:namespace,state(仓库,用来存储数据),
effects异步处理,reducers同步操作(用于接收effects的结果并存到state中),subscription订阅(用于监听页面的跳转)
state变化后,连接此model的组件所接收到的参数会自动渲染
*/
const FirstExampleModel: ExampleModelType = {
//本model唯一标识,命名标准:不能有'-',可以有'_',可以有大写
//个人标准:以后都以'Model_'开头
namespace: 'Model_FirstExampleModel',//Model_ExampleModel里面存的是state
state: {
list: [],
},
//命名规范(个人):函数功能+FromServices+Effects
effects: {
*getListFromServicesEffects({ payload }:any, { call, put }:any): Generator{//: Generator解决yield报红
console.log("进入")
const result:any = yield call(function,token,payload.value);
yield put({
type: 'getListReducers',
payload: ['first'],
});
payload.callback('出来')//调用组件中的调用函数中的callback函数
},
},
//命名规范(个人):函数功能+Reducers
reducers: {
getListReducers(state, {payload}) {
//返回形式return {...state},必须是"...state",否则报错
console.log(state)
return {...state,list:payload};//将payload赋值给list
},
},
subscriptions: {
// setup({dispatch, history}: any) {
// return history.listen(({pathname}: any) => {
// if (pathname === '/') {
// dispatch({
// type: 'getListReducers',
// });
// }
// });
// },
}
};
/*
model结构
-----------------------------------------------------------------------------------------------------------------
*/
export default FirstExampleModel;
函数组件:FirstExample
import {connect} from "umi";
const FirstExample = (props:any) =>{
console.log(props.Model_FirstExampleModel)
const clickTry = (props:any) =>{
if (props.dispatch){
props.dispatch({
//路径:model的namespace+effects函数名
type: 'Model_FirstExampleModel/getListFromServicesEffects',
payload: {
callback: (value: any) => {
console.log(value);
},
},
})
}
console.log(props.Model_FirstExampleModel)
}
return (
<div>
<a onClick={()=>{clickTry(props)}}>FirstExample</a>
</div>
);
}
//连接的参数名必须是model的namespace
export default connect(({ Model_FirstExampleModel }: any) => ({
Model_FirstExampleModel,
}))(FirstExample);
SecondExampleModel
import {Reducer, Effect, Subscription} from "umi";
/*
-----------------------------------------------------------------------------------------------------------------
本model用来演示简便的流程,即不通过reducers和state仓库直接在effects中返回
①组件连接仓库;
②组件通过connect连接后给组件自动传的参数dispatch访问仓库中的异步函数effects;
③effects获取到数据后调用callBack回调函数返回数据
④组件通过callback获取回调数据
-----------------------------------------------------------------------------------------------------------------
*/
/*
-----------------------------------------------------------------------------------------------------------------
ts规范:对变量进行声明
*/
//对state声明,state用于仓库存储数据,也就是说state就是仓库
export type StateType = {
list?: any[];
};
//对model声明
export type ExampleModelType = {
namespace: string;
state: StateType;
effects: {
getListFromServicesEffects: Effect;
};
reducers: {
};
subscriptions: {
}
};
/*
ts规范:对变量进行声明
-----------------------------------------------------------------------------------------------------------------
*/
/*
-----------------------------------------------------------------------------------------------------------------
model结构:namespace,state(仓库,用来存储数据),
effects异步处理,reducers同步操作(用于接收effects的结果并存到state中),subscription订阅(用于监听页面的跳转)
state变化后,连接此model的组件所接收到的参数会自动渲染
*/
const SecondExampleModel: ExampleModelType = {
//本model唯一标识,命名标准:不能有'-',可以有'_',可以有大写
//个人标准:以后都以'Model_'开头
namespace: 'Model_SecondExampleModel',//Model_ExampleModel里面存的是state
state: {
list: [],
},
//命名规范(个人):函数功能+FromServices+Effects
effects: {
*getListFromServicesEffects({ payload }:any, { call, put }:any) : Generator{//: Generator解决yield报红
console.log("进入")
const result:any = yield call(function,token,payload.value);
payload.callback(['second'])
},
},
//命名规范(个人):函数功能+Reducers
reducers: {
},
subscriptions: {
}
};
/*
model结构
-----------------------------------------------------------------------------------------------------------------
*/
export default SecondExampleModel;
3.3_Service模板
import {
methods,
request,
request_address_loacl,
} from '../request';//设置自己的request路径
const actions = {
getStudentsList: {
name: 'getStudentsList',
url: '/class-studentinfo-s22-z2861/getAllStudentList',//后端接口
source: 'local',
},
sendEmailToServer: {
name: 'sendEmailToServer',
url: '/class-studentinfo-s22-z2861/sendMail',
source: 'local',
}
};
const customRequest = ( action: any, formdata: any = null) => {
let options: any = {
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json'},
};
let url = action.url;
switch (action.name) {
case actions.getStudentsList.name:
options['method'] = methods.get;
break;
case actions.sendEmailToServer.name:
options['method'] = methods.post;
options['body'] = JSON.stringify(formdata);
break;
default:
}
if (action.source == 'local') {
return request(request_address_loacl + url, options);
}
};
export function getStudentsList() {
return customRequest(actions.getStudentsList);
}
export function sendEmailToServer(formdata: any) {
console.log(formdata)
return customRequest(actions.sendEmailToServer,formdata);
}
5_Request模板
在package.json中加入依赖"es6-promise": "^4.2.8"
//请求
// import fetch from 'dva';
require('es6-promise').polyfill();
require('isomorphic-fetch');
const serverDomain_loacl = 'http://10.1.40.84:7781';
export const request_address_loacl = serverDomain_loacl;
export const methods = {
get: 'GET',
post: 'POST',
put: 'PUT',
patch: 'PATCH',
delete: 'DELETE',
};
export const assemble1QueryParams = (formdata: any) => {
let url = '';
let count = 0;
Object.keys(formdata).map((value: any, index: any) => {
// console.log(value);
let key_temp = value;
let value_temp = formdata[value];
if (formdata[value]) {
// console.log(formdata[value]);
if (count == 0) {
url = url + '?' + key_temp + '=' + value_temp;
} else {
url = url + '&' + key_temp + '=' + value_temp;
}
count++;
}
});
return url;
};
function parseJSON(response: any) {
// console.log(response.json());
return response.json();
}
function checkStatus(response: any) {
// console.log(response);
if (response.status >= 200 && response.status < 300) {
let token = response.headers.get('Authorization');
if (token) {
localStorage.setItem('token', token);
}
return response;
} else {
// console.log(response);
// response.code = response.status;
// response.msg = response.error;
// console.log(response);
return response;
}
// const error = new Error(response.statusText);
// error.response = response.json();
// // return error.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 function request(url: any, options: any) {
console.log(url, options)
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then((data) => ({data}))
.catch((err) => ({err}));
}
// .then(parseJSON)