dva入手指南

由于积分商城项目接触dva搭建的项目,因为和之前使用vue框架不一样,边完成需求,边学习框架,现对学习过程作一个记录,但愿对后来接触dva的小伙伴有所帮助,有什么错误的地方,你们一块儿纠正。javascript

first of all

照惯例介绍一下dva是什么

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,而后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,因此也能够理解为一个轻量级的应用框架。css

官方解释是这样,在我理解,dva是一个对react进行简单封装,更方便进行开发的react框架。html


因为都是框架,咱们大部分项目是vue进行的,可能小伙伴们对vue更加熟悉,在后续我也会根据vue进行对比,便于记忆。

1. 安装

dva也使用脚手架进行安装。vue

$ npm install dva-cli -g 
复制代码

是否是很熟悉,和vue相似,用法也和vue-cli相似java

2. 建立项目

$ dva new dva-quickstart
复制代码

使用newnode

3. 启动

$ cd dva-quickstart
$ npm start
复制代码

本地就跑起来了react

4. 安装antd

在使用时咱们搭配antd开发,antd为react的ui框架,有了ui框架开发起来更加方便快捷webpack

$ npm install antd babel-plugin-import --save
复制代码

注意,若是node版本高于5.4.0会出现报错git

解决办法: 1.node降版本 2.删除node_modules文件夹web

我是node5.6.0,因此

$ npm install antd babel-plugin-import --save
$ npm install
复制代码

编辑 .webpackrc,使 babel-plugin-import 插件生效。

{
+  "extraBabelPlugins": [
+   ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
+  ]
}
复制代码

目录结构

|- mock
|- node_modules
|- package.json
|- public
|- src
    |- asserts
    |- components
    |- models
    |- routes
    |- services
    |- utils
    |- router.js
    |- index.js
    |- index.css
|- .editorconfig
|- .eslintrc
|- .gitignore
|- .roadhogrc.mock.js
|- .webpackrc
复制代码
  • mock 存放用于 mock 数据的文件;
  • public 通常用于存放静态文件,打包时会被直接复制到输出目录(./dist);
  • src 文件夹用于存放项目源代码;
    • asserts 用于存放静态资源,打包时会通过 webpack 处理;
    • components 用于存放 React 组件,通常是该项目公用的无状态组件;
    • models 用于存放模型文件
    • routes 用于存放须要 connect model 的路由组件;
    • services 用于存放服务文件,通常是网络请求等;
    • utils 工具类库
    • router.js 路由文件
    • index.js 项目的入口文件
    • index.css 通常是共用的样式
  • .editorconfig 编辑器配置文件
  • .eslintrc ESLint配置文件
  • .gitignore Git忽略文件
  • .roadhogrc.mock.js Mock配置文件
  • webpackrc 自定义的webpack配置文件,JSON格式,若是须要 JS 格式,可修改成 .webpackrc.js

和vue-cli生成的目录结构相似,其中有几个不一样点强调一下

  1. model文件相似于vuex中store 用来控制状态
  2. routes文件夹至关于常见的pages,固然,这里也能够改为pages
  3. 多了mock文件夹,若是只进行本地开发,能够用过.roadhogrc.mock.js配置数据进行本地开发
  4. dva开发组件都是return形式,不像vue是单写html,这两个区别比较大,须要熟悉

5.开始开发

咱们经过vue对比进行开发,因此从咱们熟悉的开始(routes已换成pages)

// .webpackrc.js
import { resolve } from 'path'
let publicPath
module.exports = {
    publicPath,
    outputPath: './dist',
    // 按需加载antd组件
    extraBabelPlugins: [
        [
            'import',
            {
                libraryName: 'antd',
                libraryDirectory: 'es',
                style: 'css',
            },
        ],
    ],
    alias: {
        components: resolve(__dirname, './src/components'),
        utils: resolve(__dirname, './src/utils'),
        pages: resolve(__dirname, './src/pages'),
        services: resolve(__dirname, './src/services'),
        api: resolve(__dirname, './src/api'),
        models: resolve(__dirname, './src/models'),
        img: resolve(__dirname, './src/assets/img'),
    },
}

复制代码
1. 新增文件夹src/api将接口地址写在里面
|- mock
|- node_modules
|- package.json
|- public
|- src
+   |- api
    |- asserts
    |- components
    |- models
    |- pages
    |- services
    |- utils
    |- router.js
    |- index.js
    |- index.css
|- .editorconfig
|- .eslintrc
|- .gitignore
|- .roadhogrc.mock.js
|- .webpackrc
复制代码

例如:

// src/api/commodity.js
// 商品列表
export const commodityListUrl = url
复制代码
2. services 将全部后台的接口请求都写在里面,例如
// src/services/commodityService.js
import request from 'utils/request'
import {
    commodityListUrl,
} from 'api/commodity'
import { createAFordownLoad } from 'utils/methods'
/** * 获取商品列表 * * @param {*} payload * @returns */
export const getCommodityList = async payload => {
    try {
        const { result } = await request({
            url: `${commodityListUrl}`,
            data: payload,
        })
        return Promise.resolve(result)
    } catch (e) {
        return Promise.resolve(e.message)
    }
}
复制代码
3. 添加路由
// src/router.js 注意这里是示意代码 不是业务代码,在实际开发中会根据状况变化
import React from 'react';
import { Router, Route } from 'dva/router';
import commodity from './pages/list';
// 伪装有其余
import example from './pages/example';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
        <Route path="/" exact component={commodity} />
        <Route path="/example" exact component={example} />
    </Router>
  );
}

export default RouterConfig;
复制代码

dva 内置了 dynamic 方法用于实现组件的动态加载,用法以下:

import dynamic from 'dva/dynamic';

const UserPageComponent = dynamic({
  app,
  models: () => [
    import('./models/users'),
  ],
  component: () => import('./routes/UserPage'),
})
复制代码

实际使用时,能够对其进行简单的封装,不然每一个路由组件都这么写一遍很麻烦。 例如咱们能够:

import routes from '' // 把Route集中写进一个文件
function RouterConfig({ history }) {
  return (
    <Router history={history}> <Switch> {routes.map(({ path, ...dynamics }, key) => ( <Route key={key} path={path} exact component={dynamic({ app, ...dynamics })} /> ))} </Switch> </Router> ); } 复制代码
4. modal设计

Model 是 dva 最重要的部分,能够理解为 redux、react-redux、redux-saga 的封装。 一般项目中一个模块对应一个 model,一个基本的 model 以下:

import { hashHistory } from 'dva/router'
import { query } from '../services/users'
export default {
  namespace: 'commodity',

  state: {
    list: [],
  },

  subscriptions: {
    setup({ dispatch, history }) {
      // 进入页面就调用
      history.listen(location => {
        if (location.pathname === '/') {
          dispatch({
            type: 'query',
            payload: {},
          })
        }
      })
    },
  },

  effects: {
    *query({ payload }, { select, call, put }) {
      yield put({ type: 'showLoading' })
      const { data } = yield call(query)
      if (data) {
        yield put({
          type: 'querySuccess',
          payload: {
            list: data.data,
            total: data.page.total,
            current: data.page.current,
          },
        })
      }
    },
    *create() {},
    // delete为关键字
    *'delete'() {},
    *update() {},
  },

  reducers: {
    showLoading(state, action) {
      return {
        ...state,
        loading: true,
      }
    },
    showModal() {},
    hideModal() {},
    querySuccess(state, action) {
      return {
        ...state,
        ...action.payload,
        loading: false,
      }
    },
    createSuccess() {},
    deleteSuccess() {},
    updateSuccess() {},
  },
}

复制代码
  • namespace 是该 model 的命名空间,同时也是全局 state 上的一个属性,只能是字符串,不支持使用 . 建立多层命名空间。
  • state 是状态的初始值。
  • reducer 是惟一能够修改 state 的地方,由 action 触发,它有 state 和 action 两个参数。 相似vue中store的mutation
  • effects 用于处理异步操做,不能直接修改 state,由 action 触发,也可触发 action。它只能是 generator 函数,而且有 action 和 effects 两个参数。第二个参数 effects 包含 put、call 和 select 三个字段,put 用于触发 action,call 用于调用异步处理逻辑,select 用于从 state 中获取数据。 相似vue中store中的action
  • subscriptions 用于订阅某些数据源,并根据状况 dispatch 某些 action,格式为 ({ dispatch, history }, done) => unlistenFunction。
5. 组件设计(因为后台项目比较大,建议写在对应的页面component里)
6. 连通组件和modal数据
|- src
    |- api
    |- asserts
    |- components
    |- models
    |- pages
+        |- commodity
+           |- component
+                |- dataTable.js
+           |- list.js
复制代码
// src/pages/commodity/component/dataTable.js
import React, { Component } from 'react'
import { Table, Button } from 'antd' // antd 组件

class DataTable extends Component {
    render() {
        const {
            data: { list, pagination },
        } = this.props // 获取数据
        const columns = [] // 定义表头及内容
        return (
            <Table rowKey="draftSn" columns={columns} DataTable dataSource={list} pagination={pagination} /> ) } } export default DataTable 复制代码
// src/pages/commodity/list.js
import React, { Component } from 'react'
import { connect } from 'dva'
import DataTable from './component/dataTable' // 注意组件必定要大写

class CommodityList extends Component {
    state = {
        list: [],
    }
    render() {
        // 从modal获取数据
        const {
            commodity, // modal数据
            dispatch, // service方法
            location, // 能够获取url相关信息
            history,  // 路由
        } = this.props
        // 为组将传参
        const tableProps = {
            data: commodity,
            dispatch,
            history,
        }
        return (
            <div> <DataTable {...tableProps} /> </div> ) } } // 连通modal和页面数据 function mapStateToProps({ commodity }) { return { commodity } } export default connect(mapStateToProps)(CommodityList) 复制代码

final

熟悉了这些,能够愉快开始开发了。虽然上手稍微比vue难一点,可是熟悉了,写起来仍是很爽的。
相关文章
相关标签/搜索