Dva简介
dva 是一个基于 redux 和 redux-saga的数据流方案,并且为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为dva是一个轻量级的应用框架。 – 摘自官网
购物车实例
本练习使用dva+antd做了个购物车小练习,目的是为了更快上手dva,所以练习的复杂度不高,更多是基础运用开发。项目参考来源
基本配置
- 安装dva脚手架
npm install dva-cli -g
- 创建新项目
dva new shopping-cart-dva
cd shopping-cart-dva
- 在dva项目里使用
antd
yarn add antd babel-plugin-import
- 编辑
.webpackrc
,使babel-plugin-import
插件生效{ "extraBabelPlugins": [ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
- 编辑
.webpackrc
,配置proxy
属性,代理请求"proxy": { "/api": { "target": "http://localhost:3000", // 后端接口 "changeOrigin": true, // "pathRewrite": { "^/api" : "" } } }
搭建后台
本次练习主要为了熟悉dva做状态管理,对后台要求不高,只是简单地返回商品数据
yarn add cors eexpress
- 新建
server
文件夹,用express
搭建一个简单的后台- 新建
data/products.json
,存放json
格式的商品数据{ "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
,并编辑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
并行命令yarn add concurrently
- 编辑
package.json
"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
访问
-
创建商品列表路由
- 新建
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);