初探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 express
  • 新建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 (
            
      soooo many products!!!
      ); } }; // 这里的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 (
              
                
                  
                  
                  
              
            );
          }
          
          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);

你可能感兴趣的:(javascript,前端,react.js)