初探dva——购物车实例

Dva简介

dva 是一个基于 redux 和 redux-saga的数据流方案,并且为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为dva是一个轻量级的应用框架。 – 摘自官网

购物车实例

本练习使用dva+antd做了个购物车小练习,目的是为了更快上手dva,所以练习的复杂度不高,更多是基础运用开发。项目参考来源

github代码

基本配置

  • 安装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.jsindex.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);
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值