当前位置: 首页 > news >正文

住房建设网站柳州济南网站优化培训

住房建设网站柳州,济南网站优化培训,d网站建设的目的,青海微网站建设dva核心知识与实战运用 dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架! 介绍 | DvaJS 易学易用,仅有 6 个 api&…

dva核心知识与实战运用

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架!

介绍 | DvaJS

  • 易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API

  • elm 概念,通过 reducers, effects 和 subscriptions 组织 model

  • 插件机制,比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading

  • 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR

1. 如何使用dva?

1.1 在create-react-app的基础上使用dva

在create-react-app脚手架的基础上,额外安装的内容:

  • 无需手动进行antd按需导入

  • 无需安装:redux及redux-saga、react-redux、react-router-dom等,dva把这些东西都集成好了,安装一个dva就相当于安装了这些全部东西!!

    • react-router-dom使用的是v4版本「4.3.1」

    • redux使用的是 v3.7.2「我们之前使用的都是v4.0」

    • 集成的配套插件版本有点低

    • 在React18的脚手架中使用dva会有警告错误!!

  • history 是控制路由模式的

  • 其余的按照之前讲的配置方案去配置webpack,包括:less、跨域代理、兼容、响应式布局等

 注意安装的版本

{"dependencies": {"antd": "^5.0.0","antd-icons": "^0.1.0-alpha.1","dva": "^2.4.1","http-proxy-middleware": "^2.0.6","less": "^4.1.3","less-loader": "^8.1.1","prop-types": "^15.8.1","styled-components": "^5.3.6","history": "4.10.1",......}
}

项目的结构目录,可以依然沿用之前的命名风格:

  • api 接口管理和请求封装

  • assets 静态资源文件

  • router 路由统一配置

  • store redux公共状态管理

  • views 普通业务组件

  • components 公共业务组件

  • index.jsx 入口

  • setupProxy.js 跨域代理

但是有很多文件的编写方式和之前是不一样的!!


index.js入口

import dva from 'dva';
import createHistory from 'history/createHashHistory';
import RouterConfig from './router';
import voteModel from './store/voteModel';// 初始化配置
const app = dva({// 设置路由模式{默认HASH路由}history: createHistory()
});
// 使用插件
app.use({});
// redux公共状态管理
app.model(voteModel);
// 路由配置
app.router(RouterConfig);
// 启动dva
app.start('#root');

router/index.js 配置页面入口和路由

import React from 'react';
import { Router, Route, Switch, Redirect } from 'dva/router';
import Vote from '../views/Vote';
import Demo from '../views/Demo';
/* ANTD */
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import '../assets/reset.min.css';function RouterConfig({ history }) {return (<ConfigProvider locale={zhCN}><Router history={history}><Switch><Route path="/" exact component={Vote} /><Route path="/demo" component={Demo} /><Redirect to="/" /></Switch></Router></ConfigProvider>);
}
export default RouterConfig;

store/voteModel.js 配置每个模块的Model,包含:状态、reducer、异步派发的方法等

import _ from '../assets/utils';
const delay = (interval = 1000) => {return new Promise(resolve => {setTimeout(() => {resolve();}, interval);});
};
export default {namespace: 'vote',state: {supNum: 10,oppNum: 5},reducers: {support(state, action) {state = _.clone(true, state);let { payload = 1 } = action;state.supNum += payload;return state;},oppose(state, action) {state = _.clone(true, state);let { payload = 1 } = action;state.oppNum += payload;return state;}},effects: {supportAsync: [function* ({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'support',payload});},{ type: 'takeLatest' }],*opposeAsync({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'oppose',payload});}}
};

在组件中如何使用呢?

import React from "react";
import styled from "styled-components";
import { Button } from 'antd';
import { connect } from 'dva';// 样式处理
const VoteBox = styled.div`...
`;const Vote = function Vote(props) {let { supNum, oppNum, dispatch } = props;return <VoteBox><div className="header"><h2 className="title">React是很棒的前端框架</h2><span className="num">{supNum + oppNum}</span></div><div className="main"><p>支持人数:{supNum}人</p><p>反对人数:{oppNum}人</p></div><div className="footer"><Button type="primary"onClick={() => {dispatch({type: 'vote/supportAsync',payload: 10});}}>支持</Button><Button type="primary" dangeronClick={() => {dispatch({type: 'vote/opposeAsync'});}}>反对</Button></div></VoteBox>;
};
export default connect(state => state.vote)(Vote);

1.2 但是更多的时候,我们会直接使用 dva 自带的脚手架创建项目 

dva脚手架创建的项目是基于 roadhog /rəʊd hog/ 进行webpack的配置!!
roadhog是一个cli工具,提供server、 build和test三个命令,分别用于本地调试和构建,并且提供了特别易用的mock功能。命令行体验和create-react-app一致,配置略有不同,比如默认开启 css modules,然后还提供了JSON格式的配置方式!
$ npm install dva-cli -g
$ dva -v
$ dva new my-project

 

 package.json

{"private": true,"scripts": {"start": "cross-env PORT=3000 HOST=127.0.0.1 roadhog server", //开发环境启动"build": "roadhog build", //生产环境打包"lint": "eslint --ext .js src test", //单元测试"precommit": "npm run lint"},"dependencies": {"@babel/polyfill": "^7.12.1","antd": "4.24.7", //注意版本用v4「不是最新的v5」"antd-icons": "^0.1.0-alpha.1","babel-plugin-import": "^1.13.5", //antd按需导入"dva": "^2.4.1","history": "4.10.1", //管理路由模式的「用v4不是最新的v5版本」"lib-flexible": "^0.3.2","postcss-pxtorem": "5.1.1","prop-types": "^15.8.1","qs": "^6.11.0","react": "^16.2.0", //react使用的是v16版本"react-dom": "^16.2.0","styled-components": "^5.3.6"},"devDependencies": {"babel-plugin-dva-hmr": "^0.3.2", //热更新"cross-env": "^7.0.3","less": "4.1.3","less-loader": "8.1.1",...}
}

 修改webpack配置项

修改启动的域名和端口号:设置环境变量即可

  • PORT

  • HOST

  • HTTPS 是否开启https,默认关闭

  • BROWSER 设为none时不自动打开浏览器

  • CLEAR_CONSOLE 设为none时清屏

“start”: “cross-env PORT=3000 HOST=127.0.0.1 roadhog server”,

.webpackrc改为.webpackrc.js,这样就可以按照JS方式去编写配置项了!!

  • 修改入口、出口、打包配置等

  • Antd按需导入

  • 配置跨域代理

  • 配置响应式布局方案

  • 配置less

  • 不同环境下的配置

  • 浏览器兼容

  • ……

PC配置:

import px2rem from 'postcss-pxtorem';
export default {/* 基础配置 */"entry": "src/index.js", //配置多入口:src/enter/*.js"outputPath": "./dist","publicPath": "/","hash": true,"html": {"template": "./public/index.ejs"},/* 配置LESS */"disableCSSModules": true,/* 配置PX转REM */"extraPostCSSPlugins": [px2rem({"rootValue": 75,"propList": ['*']})],/* 配置BABEL的插件 */"extraBabelPlugins": [// antd按需导入["import",{"libraryName": "antd","libraryDirectory": "es","style": "css"}],// 配置PX转REM["styled-components-px2rem",{"rootValue": 75}]],/* 配置跨域代理 */"proxy": {"/api": {"target": "https://news-at.zhihu.com/api/4","changeOrigin": true,"ws": true,"pathRewrite": {"/api": ""}}},/* 不同环境下的不同配置 */"env": {"development": {"extraBabelPlugins": ["dva-hmr"]}}
};

 浏览器兼容:默认情况下,ES6语法和CSS3的兼容已经处理,如果想处理ES6内置API的兼容,则导入@babel/polyfill即可「入口导入」!!

移动配置:

import px2rem from 'postcss-pxtorem';
export default {// 对于css的处理 disableCSSModules: true,disableCSSSourceMap: true,/* 基础配置 */"entry": "src/index.js", //配置多入口:src/enter/*.js"outputPath": "./dist","publicPath": "/","hash": true,// "html": {//     "template": "./public/index.ejs"// },/* 配置LESS */"disableCSSModules": true,/* 配置PX转REM */"extraPostCSSPlugins": [px2rem({"rootValue": 75,"propList": ['*']})],/* 配置BABEL的插件 */"extraBabelPlugins": [// antd按需导入["import",{"libraryName": "antd","libraryDirectory": "es","style": "css"}],// 配置PX转REM["styled-components-px2rem",{"rootValue": 75}]],/* 配置跨域代理 */"proxy": {"/api": {"target": "https://localhost:8888","changeOrigin": true,"ws": true,"pathRewrite": {"/api": ""}}},/* 不同环境下的不同配置 */"env": {"development": {"extraBabelPlugins": ["dva-hmr"]}}
};

2. dva中的路由配置

index.js

import dva from 'dva';
/*安装history模块「安装v4.10.1版本,不建议安装最新版本」$ yarn add history@4.10.1默认开启的就是HASH路由,如果想使用History路由,则导入createBrowserHistory!!
*/
import createHistory from 'history/createHashHistory';const app = dva({// 指定路由模式history: createHistory()});
...
app.router(require('./router').default);
app.start('#root');

router.js

/* 
dva/router中包含了react-router-dom v5版本中所有API,以及react-router-redux中的的API 
*/
import React from 'react';
import { Router, Route, Switch, Redirect } from 'dva/router';
import Vote from './routes/Vote';
import Demo from './routes/Demo';
import Personal from './routes/Personal';/* ANTD */
...const RouterConfig = function RouterConfig({ history }) {return <ConfigProvider locale={zhCN}><Router history={history}><Switch><Route path="/" exact component={Vote} /><Route path="/demo" component={Demo} /><Route path="/personal" component={Personal} /><Redirect to="/" /></Switch></Router></ConfigProvider>;
}
export default RouterConfig;

路由懒加载

路由懒加载主要使用 dva下的dynamic

API | DvaJS

import React from 'react';
import { Router, Route, Switch, Redirect } from 'dva/router';
import Vote from './routes/Vote';
import dynamic from 'dva/dynamic'; //实现动态组件的API/* ANTD */
...const RouterConfig = function RouterConfig({ history, app }) {/* 异步组件 */const DemoAsync = dynamic({app,models: () => [import(/* webpackChunkName:"demo" */ './models/demoModel')],component: () => import(/* webpackChunkName:"demo" */ './routes/Demo')});const PersonalAsync = dynamic({app,models: () => [import(/* webpackChunkName:"personal" */ './models/personalModel')],component: () => import(/* webpackChunkName:"personal" */ './routes/Personal')});return <ConfigProvider locale={zhCN}><Router history={history}><Switch><Route path="/" exact component={Vote} /><Route path="/demo" component={DemoAsync} /><Route path="/personal" component={PersonalAsync} /><Redirect to="/" /></Switch></Router></ConfigProvider>;
}
export default RouterConfig;

配置路由表和二级路由

routerRoutes.js 路由表

import Vote from './routes/Vote';
import dynamic from 'dva/dynamic';
/* 配置路由懒加载 */
const lazy = function lazy(models, component) {return dynamic({app: window.app, //在入口处挂载到window上models,component});
};const routes = [{path: '/',exact: true,component: Vote,meta: { title: '首页' }
}, {path: '/demo',component: lazy(() => [import(/* webpackChunkName:"demo" */ './models/demoModel')],() => import(/* webpackChunkName:"demo" */ './routes/Demo')),meta: { title: '测试页' }
}, {path: '/personal',component: lazy(() => [import(/* webpackChunkName:"personal" */ './models/personalModel')],() => import(/* webpackChunkName:"personal" */ './routes/Personal')),meta: { title: '个人中心' },/* 二级路由 */children: [{redirect: true,exact: true,from: '/personal',to: '/personal/order'}, {path: '/personal/order',component: lazy(() => [],() => import(/* webpackChunkName:"personal" */ './routes/personal/MyOrder')),meta: { title: '个人中心-我的订单' }}, {path: '/personal/profile',component: lazy(() => [],() => import(/* webpackChunkName:"personal" */ './routes/personal/MyProfile')),meta: { title: '个人中心-我的信息' }}]
}, {redirect: true,to: '/'
}];
export default routes;

router.js

import React from 'react';
import { Router, Route, Switch, Redirect } from 'dva/router';
import routes from './routerRoutes';
/* ANTD */
...
/* 动态创建路由 */
const createRoute = function createRoute(routes) {return <Switch>{routes.map((item, index) => {let { redirect, from, to, exact, path, meta, component: Component } = item,config = {};// 重定向if (redirect) {config = { to };if (from) config.from = from;if (exact) config.exact = exact;return <Redirect {...config} key={index} />;}// 正常路由config = { path };if (exact) config.exact = exact;return <Route {...config} key={index}render={(props) => {// 修改标题let { title = '' } = meta;document.title = `${title}-珠峰培训React`;return <Component {...props} />;}} />;})}</Switch>;
};
/* 一级路由 */
const RouterConfig = function RouterConfig({ history }) {return <ConfigProvider locale={zhCN}><Router history={history}>{createRoute(routes)}</Router></ConfigProvider>;
};
/* 二级路由 */
export const childrenRouter = function childrenRouter(path) {let item = routes.find(item => item.path === path),children;if (item) children = item.children;if (!children) return null;return createRoute(children);
};
export default RouterConfig;

index.js

import dva from 'dva';
import createHistory from 'history/createHashHistory';
import voteModel from './models/voteModel';
// 1. Initialize
const app = dva({history: createHistory()
});
window.app = app;
// 2. Plugins
// app.use({});
// 3. Model
app.model(voteModel);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');

Personal.jsx

import React from "react";
import { NavLink } from 'dva/router';
import styled from "styled-components";
import { childrenRouter } from '../router';
/* 样式处理 */
const PersonalBox = styled.div`...
`;
const Personal = function Personal() {return <PersonalBox><div className="menu"><NavLink to="/personal/order">我的订单</NavLink><NavLink to="/personal/profile">我的信息</NavLink></div><div className="content">{childrenRouter('/personal')}</div></PersonalBox>;
};
export default Personal;

路由跳转及传参

     history对象中提供了路由跳转的方法

       + go

       + goBack -> go(-1)

       + goFoward -> go(1)

       + push

       + replace

路径参数:把传递的信息当做路由地址的一部分,但是需要路由地址基于”:?“设置匹配的规则
路由地址:'/personal/profile/:lx?/:name?',
history.push(`/personal/profile/0/zhufeng`); 问号传参:传递的信息会存在于地址栏中,即便用户刷新页面,依然可以获取相关传递的信息
history.push({pathname: '/personal/profile',search: 'lx=0&name=zhufeng'
}); 隐式传参:基于state把信息传递给目标组件,但是传递的信息没有在地址中存在「不丑+安全」,这样在目标组件页面刷新,传递的信息就消失了!!
history.push({pathname: '/personal/profile',state: {lx: 0,name: 'zhufeng'}
});

方案一:Link 和 NavLink
NavLink可以和路由地址进行匹配,设置选中样式!!

<div className="menu"><NavLink to="/personal/order">我的订单</NavLink><NavLink to="/personal/profile">我的信息</NavLink>
</div>

方案二:编程式导航

 routerRedux 是 react-router-redux 中提供的对象,此对象中包含了路由跳转的方法+ go/goBack/goFoward+ push/replace相比较于props.history对象来讲,routerRedux不仅可以在组件中实现路由跳转,而且可以在redux操作中实现路由的跳转!!它本身就是redux和router的结合操作!!在redux内部yield put(routerRedux.push(...))在redux外部「或者组件中」dispatch(routerRedux.push(...))一定要基于dispatch进行派发才会跳转;因为执行routerRedux.xxx方法,只会返回一个action对象;action->{type:"@@router/CALL_HISTORY_METHOD",payload:{method:'push', args:[...] }}

import React from "react";
import { routerRedux } from 'dva/router';
import { connect } from 'dva';const MyOrder = function MyOrder(props) {基于路由匹配的组件,其属性中包含:history、location、match!其中history就是实现路由跳转的+ push+ replace+ go+ goBack+ goForward如果组件不是基于路由匹配的,可以基于 withRouter 高阶函数处理即可!!let { history, dispatch } = props;return <div className="myOrderBox">我的订单<button onClick={() => {// history.push('/personal/profile');routerRedux 也可以实现路由跳转,语法和history类似好处:可以在Effects中基于 yield 实现路由跳转// Inside Effectsyield put(routerRedux.push('/logout'));// Outside Effectsdispatch(routerRedux.push('/logout'));dispatch(routerRedux.push('/personal/profile'));}}>跳转</button></div>;
};
export default connect()(MyOrder);

3. dva中Model处理 


model处理流程

1. 如果有引入多个model ,app.model可以多次执行  这样会降低首屏的加载速度

import voteModel from './models/vote';
app.model(voteModel);  
import voteModel from './models/vote2';
app.model(voteModel2);  

2. 页面需要的时候懒加载,配合路由使用懒加载 dynamic

3. Model的组成

  1. namespace 命名空间【模块名,后期获取状态和派发的标识】
  2. state 数据 【模块管理的公共状态】

  3. reducers 同步处理的方法 【已一个一个方法的模式,完成reducer中的派发行为标识的判断以及状态的更改+同步修改+外部修改+外部派发dispatch('/demo/xxx')】

  4. effects   redux-saga中异步处理方法【实现异步操作,异步派发】

  5. subscriptions  订阅【在这里订阅的方法,会在页面一加载的时候就会被通知执行,所以:我们把页面一加载就要做的事情 (和 redux 相关的)在这里处理,在这里我们可以基于 history.listen做监听,保证进入哪个组件再处理也可以】

4. 在组件中,可以基于 dva中提供的 connect高阶函数,使用公共状态及dispatch方法

入口

import voteModel from './models/voteModel';
...
app.model(voteModel);
...

基本结构

export default {// 命名空间「模块名:后期获取状态和派发都需要这个名字」namespace: 'vote',// 此模块管理的公共状态state: {},// 此模块需要判断的reducer「同步派发直达reducers」reducers: {},// 此模块需要异步派发的任务「基于redux-saga语法处理」effects: {},// 订阅方法,一开始就自动执行「获取数据,实现派发等」subscriptions: {}
};

实现计数器累计

Demo.jsx

import React from "react";
import styled from "styled-components";
import { connect } from 'dva'
import { Button } from 'antd';
...
const Demo = function Demo(props) {let { num, dispatch } = props;return <DemoBox><span className="num">{num}</span><Button type="primary"onClick={() => {dispatch({type: "demo/increment",payload: 5});}}>按钮</Button><Button type="primary" dangeronClick={() => {dispatch({type: 'demo/incrementAsync',payload: 10});}}>异步按钮</Button></DemoBox>;
};
export default connect(state => state.demo)(Demo);

demoModel.js

import _ from '../utils/utils';
const delay = (interval = 1000) => {...
};
export default {namespace: 'demo',state: {num: 0},reducers: {increment(state, action) {state = _.clone(true, state);let { payload = 1 } = action;state.num += payload;return state;}},effects: {*incrementAsync({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'increment',payload});}}
};

effects中的特殊处理

effects: {incrementAsync: [function* ({ payload }, { call, put, select }) {try {// 获取状态let { num } = yield select(state => state.demo);// 发送请求yield call(delay, 2000);// 派发任务yield put({type: 'increment',payload});} catch (err) {// 异常捕获console.log(err);}},// 指定监听的类型,默认是takeEvery「还有:takeLatest、throttle等」{ type: "takeLatest" },// { type: "throttle", ms: 1000 }]
}

subscriptions

app.model({subscriptions: {setup({ dispatch, history }) {history.listen(location => {if (location.pathname === '/demo') {dispatch({type: 'demo/increment',payload: 100});}});}}
})

懒加载的model

const delay = (interval = 1000) => {return new Promise(resolve => {setTimeout(() => {resolve();}, interval);});
};
export default {namespace: 'demo',state: {num: 10},reducers: {把原有reducer函数中的每一种switch/case情况都写成一个单独的方法「纯函数」state:获取“本模块”的公共状态action:派发时候传递的action对象「包含type和传递的其他值(一般基于payload字段传递)」我们需要把获取的state克隆一份,然后函数最后返回的值,会替换当前模块的state!!increment(state, { payload = 1 }) {/* state = { ...state };state.num += payload;return state; */return {...state,num: state.num + payload};}},effects: {redux-saga中我们基于take/takLatest/takeEvery等方式创建的监听器,此时写成一个个的“Generator函数”即可!!-> 默认是基于takeEvery的方式创建的监听器+ 方法名是我们创建的监听器名字+ 方法就是派发的任务被监听后,执行的working方法+ 此处的函数名,不要和reducers中的函数名一致,因为:每一次派发,reducers和effects中的方法都会去匹配执行!如果函数名一样,则状态修改两次!!我们一般在effects写的名字,都加Async!!方法中的参数+ action:在组件中进行派发时,传递的action对象+ 第二个参数就是redux-saga中提供的EffectsAPI,但是没有delay/debounce...+ 基于 yield select() 可以获取所有模块的公共状态yield select(state=>state.demo) 这样就是获取指定的状态信息*incrementAsync({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'increment',payload});}如果想设置不同类型的监听器,则这样写/* incrementAsync: [// 数组第一项是working函数function* ({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'increment',payload});},// 数组第二项中指定监听器的类型{ type: 'takeLatest' }// { type: 'throttle', ms: 500 }] */},demoModel是被懒加载的,只有访问了/demo这个地址(组件),demoModel才会被注册!!这里订阅的方法+ 只有进入到这个组件,Model懒加载完毕,也被注册了,subscriptions中订阅的方法才会被执行+ 而且只会执行一次,后期路由来回切换的时候,也不再执行了subscriptions: {setup() { },}
};

加载页面就注册

     这个板块的Model是加载页面时就被立即注册的 + subscriptions中写的方法,在页面一加载的时候,就会把所有设定的方法执行+ 方法就是普通函数「不能是Generator函数」+ 传递的实参对象中具备 history/dispatch 两个属性+ history:包含路由跳转和监听的history对象+ dispatch:进行派发的方法+ 如果想页面一加载「或者是指定的某个条件下」,我们就想从服务器异步获取数据,修改此模块的状态值,则可以写在subscriptions中!!subscriptions: {// 方法只有页面一加载的时候,订阅执行一次,在后期路由切换中,不再执行/* async setup({ history, dispatch }) {console.log('VOTE-SETUP');await delay(2000);dispatch({type: 'support'});} */需求改变了一下:我们想的是,在页面第一次/重新加载的时候,只有进入Vote这个组件,我们在voteModel中写的setup,以及其内部的操作,才让其生效!!setup({ history, dispatch }) {// 在Model没有懒加载的情况下,我们可以让setup函数在页面第一次加载的过程中,就订阅到事件池里,并且通知执行!!我们在setup中基于history.listen创建路由跳转监听器:第一次会执行,以后每一次路由切换也会执行!!let unlisten = history.listen(async (location) => {let { pathname } = location;if (pathname === '/') {await delay(2000);dispatch({type: 'support'});// 返回的函数就是移除此监听器的操作unlisten();}});}}

4. dva-loading插件的应用


dva-loading 会监听指定的异步请求方法,方法开始时loading状态值为 true ,异步结束后该值自动置为 false , 可用于骨架屏或某些需要 loading 状态的场景!
$ yarn add dva-loading


使用方式:

  1. npm or yarn 安装dva-loading ,并在入口Index.js中引入, 示例:import createLoading from 'dva-loading';
  2. 在入口Index.js中 app.use( createLoading ) ,示例:app.use(createLoading());
  3. 在组件context高阶函数中 state可以拿到loading ,示例:state => {
            return {
                ...state.demo,
                loading: state.loading
            };
        }
  4. 组件内指定loading对应的 effects  示例:【loading = loading.effects['demo/testAsync'];】 

打印loading:

 index.js

import createLoading from 'dva-loading';
...
app.use(createLoading());
...

models/demoModel.js

const delay = (interval = 1000) => {...
};
export default {namespace: 'demo',state: {num: 0},reducers: {test(state) {state = { ...state };state.num++;return state;}},effects: {*testAsync(action, { call, put }) {yield call(delay, 2000);yield put({type: 'test'});}}
};

组件中使用

import { connect } from "dva";
...
const Demo = function Demo({ num, loading, dispatch }) {loading = loading.effects['demo/testAsync'];return <DemoBox><span className="num">{num}</span><Button type="primary" dangerloading={loading}onClick={() => {dispatch({ type: 'demo/testAsync' });}}>异步按钮</Button></DemoBox>;
};
export default connect(state => {return {...state.demo,loading: state.loading};}
)(Demo);

中间件:

npm view xxx version 查看历史版本

这里使用 2.10.2 版本

Redux Middleware

5. 基于dva重写投票案例


voteModel.js

import _ from '../utils/utils';
const delay = (interval = 1000) => {return new Promise(resolve => {setTimeout(() => {resolve();}, interval);});
};export default {namespace: 'vote',state: {supNum: 10,oppNum: 5},reducers: {support(state, action) {state = _.clone(true, state);let { payload = 1 } = action;state.supNum += payload;return state;},oppose(state, action) {state = _.clone(true, state);let { payload = 1 } = action;state.oppNum += payload;return state;}},effects: {supportAsync: [function* ({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'support',payload});},{ type: 'takeLatest' }],opposeAsync: [function* opposeAsync({ payload }, { call, put }) {yield call(delay, 2000);yield put({type: 'oppose',payload});},{ type: 'takeLatest' }]}
};

Vote.jsx

import React from "react";
import styled from "styled-components";
import { Button } from 'antd';
import { connect } from 'dva';
...
const Vote = function Vote(props) {let { supNum, oppNum, dispatch } = props;return <VoteBox><div className="header"><h2 className="title">React是很棒的前端框架</h2><span className="num">{supNum + oppNum}</span></div><div className="main"><p>支持人数:{supNum}人</p><p>反对人数:{oppNum}人</p></div><div className="footer"><Button type="primary"onClick={() => {dispatch({type: 'vote/supportAsync',payload: 10});}}>支持</Button><Button type="primary" dangeronClick={() => {dispatch({type: 'vote/opposeAsync'});}}>反对</Button></div></VoteBox>;
};
export default connect(state => state.vote)(Vote);


文章转载自:
http://hypersthenic.c7625.cn
http://gazoomph.c7625.cn
http://unarmoured.c7625.cn
http://fanny.c7625.cn
http://leonard.c7625.cn
http://flagrance.c7625.cn
http://kalian.c7625.cn
http://inherency.c7625.cn
http://conspirator.c7625.cn
http://giftbook.c7625.cn
http://officer.c7625.cn
http://batholithic.c7625.cn
http://byproduct.c7625.cn
http://chabuk.c7625.cn
http://mudbank.c7625.cn
http://betta.c7625.cn
http://leukocytoblast.c7625.cn
http://gardant.c7625.cn
http://podzolise.c7625.cn
http://godardian.c7625.cn
http://jamshid.c7625.cn
http://eclectically.c7625.cn
http://reproducer.c7625.cn
http://remittent.c7625.cn
http://gibbsite.c7625.cn
http://phonoreception.c7625.cn
http://danceable.c7625.cn
http://edifying.c7625.cn
http://misfeasance.c7625.cn
http://gradatim.c7625.cn
http://heparin.c7625.cn
http://hotelier.c7625.cn
http://diskcomp.c7625.cn
http://pushball.c7625.cn
http://subliminal.c7625.cn
http://sphacelus.c7625.cn
http://yulan.c7625.cn
http://vagrancy.c7625.cn
http://vial.c7625.cn
http://yemenite.c7625.cn
http://transport.c7625.cn
http://warve.c7625.cn
http://alphonso.c7625.cn
http://pancreozymin.c7625.cn
http://leprology.c7625.cn
http://xylonite.c7625.cn
http://radiolucency.c7625.cn
http://ganelon.c7625.cn
http://unitarity.c7625.cn
http://blackguard.c7625.cn
http://rotatory.c7625.cn
http://absorbedly.c7625.cn
http://peachy.c7625.cn
http://wheat.c7625.cn
http://tutor.c7625.cn
http://dentalium.c7625.cn
http://overhit.c7625.cn
http://gneiss.c7625.cn
http://acetophenetide.c7625.cn
http://seismometry.c7625.cn
http://squabby.c7625.cn
http://conversationist.c7625.cn
http://transire.c7625.cn
http://gingery.c7625.cn
http://dislodge.c7625.cn
http://feather.c7625.cn
http://oxalacetic.c7625.cn
http://tetracycline.c7625.cn
http://endways.c7625.cn
http://inflammation.c7625.cn
http://causeuse.c7625.cn
http://pyxie.c7625.cn
http://undiscussed.c7625.cn
http://hyphenise.c7625.cn
http://radionuclide.c7625.cn
http://kiblah.c7625.cn
http://phytotomy.c7625.cn
http://idealisation.c7625.cn
http://substitutive.c7625.cn
http://misgive.c7625.cn
http://crubeen.c7625.cn
http://gasdynamics.c7625.cn
http://preset.c7625.cn
http://counterpose.c7625.cn
http://stelae.c7625.cn
http://solon.c7625.cn
http://tagus.c7625.cn
http://yech.c7625.cn
http://gentlemanatarms.c7625.cn
http://rhytidectomy.c7625.cn
http://fughetta.c7625.cn
http://complimental.c7625.cn
http://wellingtonian.c7625.cn
http://squirrelfish.c7625.cn
http://episepalous.c7625.cn
http://activex.c7625.cn
http://unpaved.c7625.cn
http://disastrous.c7625.cn
http://pinstripe.c7625.cn
http://sesquicentenary.c7625.cn
http://www.zhongyajixie.com/news/79598.html

相关文章:

  • 做模版网站爱站网长尾关键词
  • 网站打不开怎么处理北京seo优化推广
  • 网站空间免费 优帮云怎么免费创建个人网站
  • 个人网站主页模板郑州网站推广排名公司
  • 交友网站可以做微信朋友圈吗web制作网站的模板
  • 找个男做那个视频网站好自助建站系统软件
  • wordpress上删除主题浙江专业网站seo
  • 网站建设与制作竞价推广平台
  • 页面模板不包括上海seo网站推广公司
  • 福田网站建设哪家公司靠谱关键词歌词任然
  • wordpress多域名多站点百度极速版app下载
  • 网页设计教程谁的好迈步者seo
  • 湖北专业网站建设质量保障免费建站平台哪个好
  • 单页网站规划设计书茂名网络推广
  • html5开发的网站许昌seo公司
  • 做外挂网站seo和sem分别是什么
  • 上海 房地产网站建设seo舆情优化
  • 响应式网站建设公司深圳关键词推广
  • 高端企业网站建设谷歌sem服务商
  • 做网站中的镜像是什么google移动服务应用优化
  • 婚礼顾问网站介绍模版seo网络推广公司排名
  • 做性的视频网站搜索引擎优化实训报告
  • 娱乐平台类网站怎做关键词青岛seo计费
  • 网站开发的项目实战怎么做公司网站推广
  • 微信商家怎么开通安卓优化大师清理
  • 徐州做网站那家好网络推广违法吗
  • 政府门户网站有哪些seo关键词推广优化
  • 微信小程序app下载关键词优化的五个步骤
  • 专业做网站app的公司有哪些sem培训
  • 哪些网站可以做批发seo页面链接优化