dva使用及項目搭建

1、簡介

  本文將簡單分析dva腳手架的使用及項目搭建過程。css

  首先,dva是一個基於redux和redux-saga的數據流方案,而後爲了簡化開發體驗,dva還額外內置了react-router和fetch,因此也能夠理解爲一個輕量級的應用框架。vue

2、特性

  易用易學、elm概念、插件機制、支持HMR。react

3、環境搭建

一、首先安裝dva-cli

npm install dva-cli -g

二、初始化項目

dva new dva-quickstart
cd dva-quickstart
npm start

三、引入antd

  經過 npm 安裝 antd 和 babel-plugin-import 。babel-plugin-import 是用來按需加載 antd 的腳本和樣式的.webpack

npm install antd babel-plugin-import --save

四、按需加載,找到根目錄下面的.webpackrc文件,並在文件中添加插件配置。

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

五、試引入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);

4、項目目錄結構介紹

一、目錄結構

assets目錄:通常做爲靜態文件存儲目錄,好比圖片或者css;web

components:組件目錄;vuex

models:應用邏輯層,可存放公共的數據以及邏輯,相似於vuex;npm

pages(routes):頁面路由存放文件夾;redux

services:頁面API請求數據;babel

utils:公共方法的封裝;網絡

index.js:入口文件;

router.js:路由文件

二、具體文件介紹

2.一、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);

5、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 的映射關係。

6、dispatch方法

  dispatch 是一個函數方法,用來將 Action 發送給 State。

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

  type:方法名;

  payload:參數

  dispatch 方法從哪裏來?被 connect 的 Component 會自動在 props 中擁有 dispatch 方法。

 7、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

8、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');

相關文章
相關標籤/搜索