• dva使用及项目搭建


    一、简介

      本文将简单分析dva脚手架的使用及项目搭建过程。

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

    二、特性

      易用易学、elm概念、插件机制、支持HMR。

    三、环境搭建

    1、首先安装dva-cli

    npm install dva-cli -g

    2、初始化项目

    dva new dva-quickstart
    cd dva-quickstart
    npm start

    3、引入antd

      通过 npm 安装 antd 和 babel-plugin-import 。babel-plugin-import 是用来按需加载 antd 的脚本和样式的.

    npm install antd babel-plugin-import --save

    4、按需加载,找到根目录下面的.webpackrc文件,并在文件中添加插件配置。

    "extraBabelPlugins": [
        ["import", { "libraryName": "antd", "style": "css" }]
    ]

    5、试引入ant 组件button

    import React from 'react';
    import { connect } from 'dva';
    import styles from './IndexPage.css';
    
    import { Button } from 'antd'
    
    function IndexPage() {
      return (
        <div className={styles.normal}>
            <h1 className={styles.title}>Yay! Welcome to dva!</h1>
            <Button type="primary">primary</Button>
            <div className={styles.welcome} />
            <ul className={styles.list}>
                <li>Getting Started</li>
            </ul>
        </div>
      );
    }
    
    IndexPage.propTypes = {
    };
    
    export default connect()(IndexPage);

    四、项目目录结构介绍

    1、目录结构

    assets目录:一般作为静态文件存储目录,比如图片或者css;

    components:组件目录;

    models:应用逻辑层,可存放公共的数据以及逻辑,类似于vuex;

    pages(routes):页面路由存放文件夹;

    services:页面API请求数据;

    utils:公共方法的封装;

    index.js:入口文件;

    router.js:路由文件

    2、具体文件介绍

    2.1、index.js  入口文件

    import dva from 'dva';
    import './index.css';
    
    // 1. Initialize
    const app = dva();
    
    // 2. Plugins
    // app.use({});
    
    // 3. Model
    app.model(require('./models/example').default);
    app.model(require('./models/todos').default);
    
    // 4. Router
    app.router(require('./router').default);
    
    // 5. Start
    app.start('#root');

    2.2 router.js路由文件

    import React from 'react';
    import { Route, Switch } from 'dva/router';
    
    import dynamic from 'dva/dynamic' // 路由按需加载
    import { ConnectedRouter } from 'react-router-redux';
    import App from './pages/App'
    
    function RouterConfig({ history,app }) {
        const IndexPage = dynamic({
            app,
            component:(()=> import('./pages/IndexPage/IndexPage'))
        })
        const Users = dynamic({
            app,
            component:(()=> import('./pages/UserPage/UserPage'))
        })
        const List = dynamic({
            app,
            component:(()=> import('./pages/ListPage/ListPage'))
        })
        return (
            <ConnectedRouter history={history}>
                <App>
                    <Switch>
                        <Route path="/" exact component={IndexPage}/>
                        <Route path="/users" exact component={Users}></Route>
                        <Route path="/list" exact component={List}></Route>
                    </Switch>
                </App>
            </ConnectedRouter>
        );
    }

    2.3 页面组件IndexPage.js

    import React from 'react';
    import { connect } from 'dva';
    import styles from './IndexPage.css';
    
    import { Button } from 'antd'
    
    function IndexPage() {
      return (
        <div className={styles.normal}>
            <h1 className={styles.title}>Yay! Welcome to dva!</h1>
            <Button type="primary">primary</Button>
            <div className={styles.welcome} />
            <ul className={styles.list}>
                <li>Getting Started</li>
            </ul>
        </div>
      );
    }
    
    IndexPage.propTypes = {
    };
    
    export default connect()(IndexPage);

    五、connect()方法介绍

      connect 是一个函数,绑定 State 到 View。

    import { connect } from 'dva';
    
    function mapStateToProps(state) {
      return { todos: state.todos };
    }
    connect(mapStateToProps)(App);

      connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。

      connect 方法传入的第一个参数是 mapStateToProps 函数,mapStateToProps 函数会返回一个对象,用于建立 State 到 Props 的映射关系。

    六、dispatch方法

      dispatch 是一个函数方法,用来将 Action 发送给 State。

    dispatch({
      type: 'click-submit-button',
      payload: this.form.data
    })

      type:方法名;

      payload:参数

      dispatch 方法从哪里来?被 connect 的 Component 会自动在 props 中拥有 dispatch 方法。

     七、model层介绍

      比较常用的model成如下

    {
      namespace: 'count',
      state: 0,
      reducers: {
        add(state) { return state + 1 },
      },
      effects: {
        *addAfter1Second(action, { call, put }) {
          yield call(delay, 1000);
          yield put({ type: 'add' });
        },
      },
    }

    1.namespace:命名空间;当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成

    2.state:该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出

    3.reducers: Action 处理器,处理同步动作,用来算出最新的 State;

    4.effects:Action 处理器,处理异步动作

     注:函数名前边带一个*号,是一个生成器(Generator )函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)。

    dva 提供多个 effect 函数内部的处理函数,比较常用的是 call 和 put

      call:执行异步函数

      put:发出一个 Action,类似于 dispatch

    八、demo  TODOLIst  实现

    1.首先在components下新建一个TodoList.js文件

    import React from 'react';
    class TodoList extends React.Component{
        constructor(props) {
            super(props);
            this.state={
                value:''
            }
        }
        addTodo(e){
            if (e.keyCode===13) {
                const todo = e.target.value;
    
                this.props.dispatch({
                    type: 'todos/addTodo',
                    payload: todo
                })
                this.setState({
                    value: ''
                })
            }
        }
        deleteTodo(index){
            this.props.dispatch({
                type: 'todos/deleteTodo',
                payload: index
            })
        }
        render() {
            const todoList = this.props.todoList.map((val, index) => {
        
              return <div key={index}>
                <span>{val.value}</span>
                <button onClick={() => this.deleteTodo(index)}>X</button>
              </div>
            });
        
            let count = 0;
        
            this.props.todoList.map(item => count = !item.finished ? count + 1 : count);
        
            return (
              <div>
                <h3>待办事项有:{count}</h3>
                <input placeholder="please input"
                       value={this.state.value}
                       onChange={(e) => this.setState({value: e.target.value})}
                       onKeyDown={(e) => this.addTodo(e)}/>
                <div>
                  {todoList}
                </div>
              </div>
            )
        }
    }
        
    export default TodoList;

    代码中:通过dispatch 派送一个action,type为action名称,payload为传递参数

     this.props.dispatch({
                    type: 'todos/addTodo',
                    payload: todo
                })

    2.新建路由页面LIstPage.js

    import {connect} from 'dva';
    import TodoList from '../../components/TodoList';
    
    const mapStateToProps = (state) => {
    
        return {
          todoList: state.todos.todoList
        }
      };
      
      export default connect(mapStateToProps)(TodoList);

    通过mapStateToProps 方法将model里的todoList放回到页面组件的props.todoList;

    3.新建一个model todos.js

    import queryString from 'query-string';
    import * as todoService from '../services/todo'
    
    export default {
        namespace: 'todos',
        state: {todoList: []},
        reducers: {
            save(state, {payload: {todoList}}) {
                return {...state, todoList}
            }
        },
        effects: {
            * addTodo({payload: value}, {call, put, select}) {
                // 模拟网络请求
                const data = yield call(todoService.query, value);
                let tempList = yield select(state => state.todos.todoList);
                let todoList = [];
                todoList = todoList.concat(tempList);
                const tempObj = {};
                tempObj.value = value;
                tempObj.id = todoList.length;
                todoList.push(tempObj);
                yield put({type: 'save', payload: {todoList}})
            },
            * deleteTodo({payload: index}, {call, put, select}) {
                const data = yield call(todoService.query, index);
                let tempList = yield select(state => state.todos.todoList);
                let todoList = [];
                todoList = todoList.concat(tempList);
                todoList.splice(index, 1);
                yield put({type: 'save', payload: {todoList}})
            },
        },
        subscriptions: {
            setup({dispatch, history}) {
                // 监听路由的变化,请求页面数据
                return history.listen(({pathname, search}) => {
                    const query = queryString.parse(search);
                    let todoList = [];
            
                    if (pathname === 'todos') {
                        dispatch({type: 'save', payload: {todoList}})
                    }
                })
            }
        }
    }

    一般来说,effects做主要的逻辑计算,reducers做数据存储,通过复杂的逻辑计算后,把处理好的数据调用reducers的方法进行数据存储。

    4.在index.js进行model以及路由注入

    import dva from 'dva';
    import './index.css';
    
    // 1. Initialize
    const app = dva();
    
    // 2. Plugins
    // app.use({});
    
    // 3. Model
    app.model(require('./models/example').default);
    app.model(require('./models/todos').default);
    
    // 4. Router
    app.router(require('./router').default);
    
    // 5. Start
    app.start('#root');

  • 相关阅读:
    基于mini2440的boa服务器移植
    主机+虚拟机ubuntu+mini2440开发板互相ping通
    poj3133 插头dp
    2015 北京网络赛 E Border Length hihoCoder 1231 树状数组 (2015-11-05 09:30)
    2015 北京网络赛 C Protecting Homeless Cats hihoCoder 1229 树状数组
    acm 2015北京网络赛 F Couple Trees 主席树+树链剖分
    hdu4777 树状数组
    hdu5517 二维树状数组
    Codeforces Round #327 (Div. 1) D. Top Secret Task
    2014-2015 ACM-ICPC, Asia Xian Regional Contest GThe Problem to Slow Down You
  • 原文地址:https://www.cnblogs.com/superSmile/p/9972344.html
Copyright © 2020-2023  润新知