- Basic
- 二、结构
- 2.1 mock假数据
- 2.2 请求的封装
- 2.3 入口文件
- 2.4 路由
- 2.5 redux
- redux-saga的常用api解析-csdn博客">umi基于redux-saga的常用api解析-csdn博客
- 文档 + github: with-dva.md">dva配置 - 文档 + github: with-dva.md
- dva-api">dva-api
- 2.7 根目录下的.webpackrc配置proxy
- 解决">2.8 dva的node_modules报错 - 解决
Basic
dva
dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。
6个api
cnpm install dva-cli -g
dva -v //(小写) ---返回: dva-cli version 0.10.1
dva new your_project_name //关键字(nwe)
cd 项目名
npm start
umi.js
umi.js 是 antd、dva、内置路由 的深度整合
npm i yarn tyarn umi -g //全局安装tyarn和umi。tyarn是yarn的国内源
建个项目的目录
cd 项目目录
## 默认配置
tyarn create @umijs/umi-app //官方工具创建项目,不允许选择配置项
# 或 npx @umijs/create-umi-app
## 自定义配置
tyarn create umi 或者 npm create umi(yarn的环境有问题就改为npm) //(目录就是项目名了) - umi来创建 可以选择配置项
cnpm i //需要自己装依赖
yarn start 默认8000端口
- 选择pro不管是ts还是js都是已有模板。选择app是简易版
新建页面 umi g page + 页面名字 即可自动在.umirc.js中添加路由信息
umi g 是 umi generate 的别名,可用于快速生成 component、page、layout 等,并且可在插件里被扩展,比如 umi-plugin-dva 里扩展了 dva:model,然后就可以通过 umi g dva:model foo 快速 dva 的 model。
.umirc.js文件 - 配置插件、路由表
- 不适合umi框架的情况:
- 需要支持 IE 8 或更低版本的浏览器
- 需要支持 React 16.8.0 以下的 React
- 需要跑在 Node 10 以下的环境中
- 有很强的 webpack 自定义需求和主观意愿
- 需要选择不同的路由方案
二、结构
2.1 mock假数据
// 1. mock/m.js(在mock目录下新建js文件)
module.pexports={
"GET /test/lsit": (req,res)=>{
res.json({
code: 200,
result: [1,2,3]
})
}
}
// 2. 在.roadhorc.mock.js中引入
export default {
...require('./mock/m.js')//结构
}
// 3. 直接就可以(在请求api中)使用`/test/lsit`这个接口
2.2 请求的封装
utils/request.js - 对fetch的封装
services/example.js - 具体的请求模块
export const Timeout = ()=>{
// 生成器函数的call必须接受一个promise(就算是一个延时器也必须写成promise形式)
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
msg: "延时器结束"
})
}, 2000);
})
}
2.3 入口文件
src/index.js 中可以注册插件、数据流、路由
- const app = dva();
// 官网上是在index.js中的这里初始化数据(redux的数据)
+ const app = dva({
+ initialState: {
+ products: [
+ { name: 'dva', id: 1 },
+ { name: 'antd', id: 2 },
+ ],
+ },
+ });
2.4 路由
dva: routes文件夹中并不是路由,而是页面-样式可以使用引入的styles; 而根目录的router.js文件是路由表
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import Test from './routes/Test'
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/test" component={Test} />
</Switch>
</Router>
);
}
export default RouterConfig;
umi: 新建页面 umi g page + 页面名字 即可自动在.umirc.js中添加路由信息
2.5 redux
models: effects-异步、reducers-同步、subscriptions-订阅(可监听动作和路由)、state-数据、namespace-命名空间 — 结构上类似于vuex了
index.js中注册模块app.model(require('./models/products').default);
export default {
namespace: 'example',// 命名空间 - 不是vue的namespaced(属性值为布尔值)
state: {},// 初始值
subscriptions: {// 监听-history是路由
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {// 异步saga - saga插件使用generator函数
*fetch({ payload }, { call, put }) { // eslint-disable-line
yield put({ type: 'save' });
},
*incAsync({ payload }, { call, put }) {
// call必须接收promise为第一个参数,还可以接受其他参数!!! - 等待结果返回
let timeout = yield call(Timeout);
console.log("延时器结束", timeout);
let mock = yield call(mockReq);
console.log("mock数据获取", mock);
let cnode = yield call(cnodeReq, ...参数);
console.log("cnode数据获取", cnode);
yield put({// 在异步中执行同步的reducers必须使用put
type: "inc", //因为在这个模块内,不用写namespace
});
}
},
reducers: {// 同步
save(state, action) {
// action = {type: "example/inc", payload: 2}
// {type: "example/inc", @@redux-saga/SAGA_ACTION: true} - 经异步put执行的reducers会带上这个属性
return { ...state, ...action.payload };
},
}
};
umi基于redux-saga的常用api解析-csdn博客
select
const store = yield select(console.log);//获取store[其中`console.log`会打印出回调内store]
const state = yield select(stroe=>stroe.userAndLogin);//根据命名空间返回相应的state
if(state.UserId)return;// 根据store或者state的数据来决定是否dispatch派发
call,apply
调用异步函数,将异步函数和函数参数作为call函数的参数传入,是阻塞的,只有等 promise回来后才能继续执行
- put 作用和 redux 中的 dispatch 相同:派发effects和reducers
- take - csdn
等待 redux dispatch 匹配某个(由用户决定) pattern 的 action 。在执行顺序执行到take语句时才会响应action。在genetator中使用take语句等待action时,generator被阻塞,等待action被分发,然后继续往下执行。
- fork 当调用fork启动一个任务时,该任务在后台继续执行,从而使得我们的执行流能继续往下执行而不必一定要等待返回。
阻塞调用和无阻塞调用: redux-saga 可以用 fork 和 call 来调用子 saga ,其中 fork 是无阻塞型调用,call 是阻塞型调用。
- redux-saga提供了几种产生副作用的方式:主要用到了有两种takeEvery和takeLates。
takeEvery:会在接到相应的action之后不断产生新的副作用。 比如,做一个计数器按钮,用户需要不断的点击按钮,对后台数据更新,这里可以使用takeEvery来触发。
takeLatest:在相同的action被触发多次的时候,之前的副作用如果没有执行完,会被取消掉,只有最后一次action触发的副作用可以执行完。
dva配置 - 文档 + github: with-dva.md
// .umirc.ts
export default {
dva: {
immer: true,// 是否启用 immer 以方便修改 reducer - 帮你做了深拷贝
hmr: false,// 是否启用 dva model 的热更新
},
}
/* 如果后台服务器返回500错误,会导致call Effect抛出异常,最终会导致generator 停止运行。
解决的方法是可以加上try catch.但是这样的话,需要每次调用时都try catch,因此需要统一异常处理。
Ant Design封装的是umi,umi封装了dva,dva封装了redux-saga。统一异常处理可以在umi中进行。
*/
*fetchColumns(_, { call, put }) {
try{
const data = yield call(fetchColumns);
yield put({
type: 'saveColumns',
payload: data,
});
}catch (e) {
onError(e)
}
}
// src/app.js dva 运行时配置: 通过 src/app.tsx 文件配置 dva 创建时的参数。
import { createLogger } from 'redux-logger';
import { message } from 'antd';
export const dva = {
config: {
onAction: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
},
};
dva-api
- redux-persist
- dva-loading ```javascript @connect(({article, loading}) => ({ …article, loading })) class ArticleList extends Component{ // …每次dispatch在组件中对ui进行loading动画 }
// loading的State Structure loading: { global: false, models: { users: false, todos: false, … }, }
**记住yiled call的传参方式参见下表↓↓↓↓↓↓: ** call传参的方式
<a name="opJiS"></a>
### [改变this指向的几种方法](https://www.cnblogs.com/web-chuan/p/9096356.html)
| 方法 | 修改this执行&传参 | 解释 |
| :--- | :--- | :--- |
| call | a.call(b,1,2) | 第一个参数表示要把this指向的新目标,第二个之后的参数其实相当于传参,参数以,隔开 (性能较apply略好)
表示要把a函数的this指向修改为b的this指向,并且运行a函数,传进去的参数是(1,2) |
| apply | a.apply(b,[1,2]) | 第一个参数同上,第二个参数接受一个数组,里面也是传参,只是以数组的方式,相当于arguments
表示要把a函数的this指向修改为b的this指向,并且运行a函数,传进去的参数是(1,2) 注意 :即使只有一个参数的话,也要是数组的形式 |
| bind | a.bind(b,1,2)
仅仅绑定this,返回的还是函数,不会自动执行,调用才会执行 | bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。 注意:多次bind无效,按第一次算 |
**总结:** (1)apply 、call 、bind 三者都是用来改变函数的this对象的指向的; (2)apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文; (3)pply 、 call 两者都可以利用后续参数传参; 但是传参的方式不一样,apply是数组,call是正常传参形式 (4)bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
<a name="PZVN9"></a>
## 2.6 组件中使用
```javascript
import {connect} from 'dva'
//组件....
const mapStateToProps=(state)=>{return{
n: state.example.n
}}
// 默认没有配置装饰器语法、无状态组件和类组件都可以使用connect高阶组件
// 注意: 不能直接在无状态组件dispatch,否则会重复调用
// 所有属性: 通过map映射传递给props
// 所有方法(包括异步&同步)不用自己connect。使用的时候通过props上的dispatch方法发动作(需加上namespace)
/*
inc=()=>{
this.props.dispatch({
type: "test/add",// type是命名空间namespace+方法名
payload: 2 // 参数
})
}
*/
export default connect(mapStateToProps)('组件名')
2.7 根目录下的.webpackrc配置proxy
{
"proxy":{
"/cnode":{
"target":"https://cnodejs.org/api/v1",
"changeOrigin": true,
"pathRewrite":{
"^/cnode": "/"
}
}
}
}