(入門)手把手帶你更簡單的使用dva

介紹(dva.js)

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

特性

  • 易學易用,僅有 6 個 api,對 redux 用戶尤爲友好,配合 umi 使用後更是下降爲 0 API
  • elm 概念,經過 reducers, effects 和 subscriptions 組織 model
  • 插件機制,好比 dva-loading 能夠自動處理 loading 狀態,不用一遍遍地寫 showLoading 和 hideLoading
  • 支持 HMR,基於 babel-plugin-dva-hmr 實現 components、routes 和 models 的 HMR

前言

個人我的理解:dva的核心實際上是 saga的封裝,將action,reducer等等所有引入到model中。vue

過多的廢話也就再也不闡述了,欲知詳情,請看官網,本文的目的就是快速開始,讓一個擁有react+redux基礎的人能夠快速使用dva.jsreact

"深刻"

配置環境安裝依賴之類的就很少說了,請看官方文檔git

dva new dva-quickstart

咱們獲得初始項目,目錄結構以下:
接下里將會逐個目錄解釋,請注意看註釋github

clipboard.png

入口文件 index.js

import dva from 'dva';   //引入依賴
import './index.css';

// 1. Initialize
const app = dva();       //初始化 dva應用

// 2. Plugins
// app.use({});          //使用中間件

// 3. Model
// app.model(require('./models/example').default); // 加載model層 (後面詳細解釋model)

// 4. Router
app.router(require('./router').default);           // 引入router 

// 5. Start
app.start('#root');                                // 掛載dva應用

基本上就是這樣,多餘的沒什麼好說的vue-router

路由匹配 router.js

import React from 'react';
import { Router, Route, Switch } from 'dva/router';  // 引入 router,用的就是 react-router
import IndexPage from './routes/IndexPage';          // 引入路由綁定的高階組件
 

// 按照從上到下的順序開始匹配url規則,匹配到了就是展現對應的組件view
function RouterConfig({ history }) {                 
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;

路由頁面 routes/IndexPage.js

在routes目錄下,是路由頁面,由多個高階組件渲染而成,固然,剛初始化的項目天然沒有寫高階組件,在後面的實戰操做中,咱們將以 路由頁面 => 高階組件 => 基礎組件 路由綁定model層,高階組件綁定路由的action事件,基礎組件綁定原生事件,在路由中觸發action更新數據流 的邏輯 完成一個簡單標準的dva過程redux

import React from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';

// 在這個方法中,咱們返回一個dom結構
// 而且 在圓括號中 能夠接受一個大對象(包含不少東西),也能夠解構 只取其中的state和dispatch,具體能夠在後面看
function IndexPage() {
  return (
    <div className={styles.normal}>
      <h1 className={styles.title}>Yay! Welcome to dva!</h1>
      <div className={styles.welcome} />
      <ul className={styles.list}>
        <li>To get started, edit <code>src/index.js</code> and save to reload.</li>
        <li><a href="https://github.com/dvajs/dva-docs/blob/master/v1/en-us/getting-started.md">Getting Started</a></li>
      </ul>
    </div>
  );
}

IndexPage.propTypes = {
};

// 這裏 connect方法就是redux的connect,後面的IndexPage表示綁定的高階組件
// 在connect的第一個括號中,是能夠拿到全部的model對象,這樣就能夠把對應的model對象綁定到咱們的高階組件上
export default connect()(IndexPage);

看了上面的註釋很蒙也不要緊,由於紙上談兵,甚至,我兵都沒有出來,你只須要知道,connect的做用及過程就行了api

數據模型 model/emample.js

export default {

  namespace: 'example',         // 命名空間 做爲 connect方法 中獲取model對象state的 id

  state: {},                    // 初始化state 

  subscriptions: {              // 訂閱
    setup({ dispatch, history }) {  // eslint-disable-line 
    },
  },

  effects: {                     // 異步action的handler
    *fetch({ payload }, { call, put }) {  // eslint-disable-line
      yield put({ type: 'save' });
    },
  },

  reducers: {                    //react-redux的reducers 用來接收action而且處理數據更新
    save(state, action) {
      return { ...state, ...action.payload };
    },
  },

};

當咱們在高階組件中經過connect綁定了高階組件和model,而且在index.js中引入這個model,就可使用標準流程:
在subscriptions方法中訂閱路由變化,當路由與高階組件相對應,調用effects請求數據,拿到數據reducer更新數據服務器

基礎組件 components/Example.js

代碼就不貼了,你們應該都知道這裏面作什麼babel

公共服務 services/example.js

這裏封裝了一些公共使用的方法

"淺出"

圖片描述

項目地址:https://github.com/zhaowanhua...

接下來,咱們將quick-start項目改形成一個按照dva標準流程的小項目(如上圖),幫助你們理解和使用
首先咱們把上面那些文件夾下面的文件所有刪乾淨

修改路由模式 index.js

import dva from 'dva';
import './index.css';
import createHistory from 'history/createBrowserHistory';

// 這個方法裏面 能夠配置router的 路由模式,好比hash或者H5 histroy,
// 具體區別能夠參考個人文章 vue-router,單頁應用原理一致的
const app = dva({
    history: createHistory()
});

// 2. Plugins
// app.use({});

// 3. Model
app.model(require('./models/List').default); // 引入model

// 4. Router
app.router(require('./router').default);

// 5. Start
app.start('#root');

建立基礎組件 components/Item.js

import React from 'react';

const Item = ({
    num,
    id,
    OnDelete
}) => {
    return (
        <li onClick={() => OnDelete(id)}>
      {num}
    </li>
    );
};


Item.propTypes = {};

export default Item;

建立數據模型 models/List.js

export default {

  namespace: 'list', // 這個namespace 是model的惟一識別id,在connect中須要使用這個綁定

  state: {},

  subscriptions: {
    setup({
      dispatch,
      history
    }) { // eslint-disable-line
      return history.listen(({
        pathname
      }) => {
        if (pathname === '/') {
          dispatch({
            type: 'fetch',
            payload: {}
          });
        }
      });
    },
  },

  effects: {
    * fetch({
      payload
    }, {
      call,
      put
    }) { // eslint-disable-line
      // 這裏僞裝 獲取到了服務器的數據
      const fetchData = [0, 1, 2, 3]
      yield put({
        type: 'save',
        list: fetchData
      });
    },
  },

  reducers: {
    // 保存
    save(state, action) {
      return {...state,
        list: action.list
      };
    },
    // 新增
    add(state, action) {
      const [..._arr] = {...state
      }.list;
      _arr.push(_arr.length)
      return {
        ...state,
        list: _arr
      }
    },
    // 刪除
    del(state, action) {
      return {
        ...state,
        list: state.list.filter((item, index) => {
          return index !== action.id
        })
      }
    },
  },

};

寫好model 是要在index.js中引入的,否則沒有效果

建立高階組件 components/List.js

import React from 'react';
import Item from './Item'

// 經過prop 把路由頁面的action觸發方法綁定過來,傳遞給子組件(OnDelete),也能夠在當前組件觸發,如OnAdd
function List({
  OnAdd,
  OnDelete,
  list
}) {
  const List = list.map((num, index) => <Item num={num} key={index} id={index} OnDelete={OnDelete}></Item>);

  return (
    <div>
      {List}
      <button onClick={OnAdd}>Add</button>
    </div>
  );
}


List.propTypes = {};

export default List;

建立路由頁面 routes/IndexPage.js

import React from 'react';
import {
  connect
} from 'dva';
import List from '../components/List'

//咱們在路由頁面裏面渲染高階組件,寫好action,經過prop傳遞給基礎組件
// 這裏引入的list 對應 model中的namespace
function IndexPage({
  dispatch,
  list
}) {
  function handleAdd() {
    dispatch({
      type: 'list/add'
    });
  }

  function handleDelete(id) {
    dispatch({
      type: 'list/del',
      id: id,
    });
  }
  return (
    <div>
      <List list={list} OnAdd={handleAdd} OnDelete={handleDelete}></List>
    </div>
  );
}


IndexPage.propTypes = {};

// 經過connect方法綁定路由頁面和model,你能夠把connect方法的第一個參數(方法裏的) 打印出來看看都有什麼東西,不要讓解構擾亂了你的眼睛,connect((obj)=>{console.log(obj)})()
export default connect(({
  list
}) => {
  return list; // 這裏是state中的list,經過connect,在每次數據更新的時候,流向咱們的view,更新視圖,你能夠在這裏"打樁",看看具體的數據流動
})(IndexPage);

總結

以上是我最近學習的想法和思考後獲得的內容,但願對你們有所幫助,寫的比較隨意,在內容中若是有問題或者想法不對,請予指正,也能夠提出新的問題,咱們共同探究.

相關文章
相關標籤/搜索