dva 是一个基于 redux 和 redux-saga的数据流方案,而且为了简化开发体验,dva 还额外内置了 react-router 和 fetch,因此也能够理解为dva是一个轻量级的应用框架。 -- 摘自官网
本练习使用dva+antd作了个购物车小练习,目的是为了更快上手dva,因此练习的复杂度不高,更可能是基础运用开发。 项目参考来源
npm install dva-cli -g
dva new shopping-cart-dva
cd shopping-cart-dva
在dva项目里使用antd
css
yarn add antd babel-plugin-import
编辑.webpackrc
,使babel-plugin-import
插件生效前端
{ "extraBabelPlugins": [ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
编辑.webpackrc
,配置proxy
属性,代理请求java
"proxy": { "/api": { "target": "http://localhost:3000", // 后端接口 "changeOrigin": true, // "pathRewrite": { "^/api" : "" } } }
本次练习主要为了熟悉dva作状态管理,对后台要求不高,只是简单地返回商品数据
yarn add cors express
新建server
文件夹,用express
搭建一个简单的后台node
新建data/products.json
,存放json
格式的商品数据react
{ "products": [ { "id": 12, "bigImage":"http://localhost:3000/images/12064273040195392_1.jpg", "smallImage": "http://localhost:3000/images/12064273040195392_2.jpg", "sku": 12064273040195392, "title": "Cat Tee Black T-Shirt", "description": "4 MSL", "availableSizes": ["S", "XS"], "style": "Black with custom print", "price": 10.9, "installments": 9, "currencyId": "USD", "currencyFormat": "$", "isFreeShipping": true } ] }
新建app.js
,并编辑webpack
const path = require('path'); const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors()); const port = 3000; app.get('/api/products', (req, res) => { res.sendFile(path.join(__dirname, 'data', 'products.json')); }); app.listen(port, () => { console.log(`[products] API listening on port ${port}.`); });
本次练习使用concurrently
并行命令git
yarn add concurrently
编辑package.json
github
"scripts": { "start": "roadhog server", "server": "nodemon server/app", "build": "roadhog build", "lint": "eslint --ext .js src test", "precommit": "yarn run lint", "dev": "concurrently \"yarn run server\" \"yarn run start\"" },
yarn run dev
命令同时启动先后端,在http://localhost:8000
访问建立商品列表路由web
新建src\routes\products\index.js
和index.css
(dva默认支持CSS Module模块化, 若是须要使用connect()
方法,须要从dva中引入)
import React, {Component} from 'react'; import { connect } from 'dva'; // 引入connect() // import { Badge, Icon } from 'antd'; 在组件中使用antd import styles from './index.css'; // 按需引入css class Products extends Component { constructor(){ super(); } render(){ return ( <div className={styles.container}> soooo many products!!! </div> ); } }; // 这里的products是namespace为products的model层 const mapStateToProps = ({ products })=>{ return { counts: products.counts } }; // 跟react-redux的connect使用方法类似 export default connect(mapStateToProps)(Products);
编辑src/router.js
import React from 'react'; import { Router, Route, Switch } from 'dva/router'; import IndexPage from './routes/IndexPage'; import Products from './routes/products'; // 使用hash前端路由模式 function RouterConfig({ history }) { return ( <Router history={history}> <Switch> <Route path="/" exact component={IndexPage} /> <Route path="/products" exact component={Products} /> </Switch> </Router> ); } export default RouterConfig;
dva已经在src/utils/request.js
中为咱们封装了fetch请求。因此咱们能够新建src/services/products.js
来封装一个向后台请求商品数据的方法
import request from '../utils/request'; export function fetch() { return request('/api/products'); }
dva 经过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions
新建src/models/products.js
import * as productsService from '../services/products'; export default { namespace: 'products', state: { list: [], // 商品列表 }, reducers: { save(state, { payload: { data: list } }) { return { ...state, list }; } }, effects: { *fetch({}, { call, put }) { const { data } = yield call(productsService.fetch); yield put({ type: 'save', payload: { data: data.products }, }); }, }, subscriptions: { setup({ dispatch, history }) { return history.listen(({ pathname }) => { if (pathname === '/products') { dispatch({ type: 'fetch' }); } }); }, }, };
到此,model层和service层咱们已经简单搭建好,能够在组件中拿到商品数据
拓展:model
中的subscription
订阅
model
中的subscription
至关于一个监听器,能够监听路由变化,鼠标,键盘变化,服务器链接变化,状态变化等,这样在其中就能够根据不一样的变化作出相应的处理,在这个subsription
中的方法名是随意定的,每次变化都会一次去调用里面的全部方法,因此一边会加相应的判断dva-loading
插件
在实际开发中,因为存在向后端请求商品数据,可能不能第一时间拿到商品数据,因此每每在数据加载的过程当中,咱们会给用户一个“正在加载中...”的提示。而dva-loading
插件能够帮咱们简化这一过程。
yarn add dva-loading
修改src/index.js
import createLoading from 'dva-loading'; app.use(createLoading());
接着咱们就能够在组件中使用
const ProductsList = ({ list, loading }) => { ... // loading的值即为true,false // 插件会自动设置数据里的 loading 状态为 true 或 false // 咱们能够经过loading的值判断是否正处于加载数据的状态,从而给用户一些提示 } export default connect(({ products, loading }) => ({ list: products.list, // 注意:products为所须要的model层的namespace loading: loading.models.products }))(ProductsList);