Dva + React + Mock 搭建項目 (主要講解DvaJs)

 

一、初始化dva

1、安裝 dva-cli

$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.1

2、創建新應用

安裝完 dva-cli 之後,就可以在命令行裏訪問到 dva 命令。現在,你可以通過 dva new 創建新應用。

$ dva new dva-quickstart

這會創建 dva-quickstart 目錄,包含項目初始化目錄和文件,並提供開發服務器、構建腳本、數據 mock 服務、代理服務器等功能。

然後我們 cd 進入 dva-quickstart 目錄,並啓動開發服務器:

$ cd dva-quickstart
$ npm start

幾秒鐘後,你會看到以下輸出:

Compiled successfully!

The app is running at:

  http://localhost:8000/

Note that the development build is not optimized.
To create a production build, use npm run build.

在瀏覽器裏打開 http://localhost:8000 ,你會看到 dva 的歡迎界面。

打開F12你會看到一個報錯,報錯來源於node/model下面的依賴包,引入方式問題,可以自行百度解決(不影響使用!)

到此,dva項目就創建完成!

基本目錄結構

 

二、dva路由設置

1、創建新頁面 src/routes/userPage.js

import React from 'react';

class userPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }
  render() {
    return (
      <div>用戶頁面</div>
    );
  }
}

export default userPage;

然後可以打開src/index.js看到第4步引入了 ./router.js

2、再打開src/routre.js ,在裏面照葫蘆畫瓢引入userPage.js

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
//引入userpage
import UserPage from './routes/userPage';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
        {/* 添加新路由 */}
        <Route path="/user"  component={UserPage} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;

瀏覽器頁面在輸入 http://localhost:8000/#/user 就可以訪問到用戶頁面了

也可以用到Link跳轉

//引入Fragment 作爲一個根組件
import React,{Fragment} from 'react';
//引入link
import { Link } from 'dva/router';

class userPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }
  render() {
    return (
      <Fragment>
         <div>用戶頁面</div>
         <br/>
         <Link to="/">跳轉首頁</Link>
      </Fragment>
    );
  }
}

export default userPage;

再次刷新頁面,點擊跳轉首頁,就可以跳回到首頁了!

怎麼樣用函數跳轉呢,接着看

//引入Fragment 作爲一個根組件
import React,{Fragment} from 'react';
//引入link
import { Link } from 'dva/router';

class userPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }
    
  //跳轉首頁函數
  handleToIndex = () =>{
    this.props.history.push('/');
  }

  render() {
    return (
      <Fragment>
         <div>用戶頁面</div>
         <br/>
         <Link to="/">跳轉首頁</Link>
         <br/>
         <br/>
         <button onClick={this.handleToIndex}>首頁</button>
      </Fragment>
    );
  }
}

export default userPage;

這樣就實現了函數跳轉頁面!

如何實現組件跳轉頁面呢,進一步操作!

在src/components創建一個新文件Child.js

import React from 'react';
//引入 withRouter
import { withRouter } from 'dva/router';

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }

  //組件跳轉首頁
  handleToIndex = () =>{
    this.props.history.push('/');
  }

  render() {
    return (
      <div>
        <div>通用組件</div>
        <button onClick={this.handleToIndex}>首頁_child</button>
      </div>
    );
  }
}

//寫入路由
export default withRouter(Child);

然後再在route/userPage.js  中引入 Child組件

//引入Fragment 作爲根節點
import React,{Fragment} from 'react';
//引入Link
import { Link } from 'dva/router';

//引入通用組件
import Child from '../components/Child';

class userPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }

  //跳轉首頁函數
  handleToIndex = () =>{
    this.props.history.push('/');
  }

  render() {
    return (
      <Fragment>
        <div>用戶頁面</div>
        <br/>
        <Link to="/">跳轉首頁</Link>
        <br/>
        <br/>
        <button onClick={this.handleToIndex}>首頁</button>
        <br/>
        <br/>
        <Child/>
      </Fragment>
    );
  }
}

export default userPage;

再點擊下組件的跳轉首頁按鈕,即可實現組件跳轉,寫這個組件是爲了講解組件如何引入路由跳轉!

現在訪問頁面地址是哈希路由,如果不想用哈希路由訪問的話,只需要修改一下src/index.js下面的配置就好了

import dva from 'dva';
import './index.css';

//修改不使用哈希路由訪問
import {createBrowserHistory as createHistory} from 'history';
// 1. Initialize
const app = dva({
  history: createHistory(),
});

// 1. Initialize
// const app = dva();

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

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

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

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

現在就可以直接用http://localhost:8000/訪問了  而不是 http://localhost:8000/#/  (這位同學別走神,認真點,別看了,就是你!!!!)

 

 

三、講解model中的reducers

reducers:以 key/value 格式定義 reducer。用於處理同步操作,唯一可以修改 state 的地方

1、現在src/models文件下創建一個文件 indexTest.js

export default {
  //命名空間
  namespace:'indexTest',
  //數據
  state:{
    name:'message'
  }
}

現在只是單純創建了一個js文件,並沒有在任何地方使用,那麼這個需要如何使用呢,打開文件src/index.js入口文件

可以看到已經註釋的第3步有一句:app.model(require('./models/xxxx').default);

2、在src/index.js中引入model  : app.model(require('./models/indexTest').default);

3、回到route/IndexPage.js中修改js

import React from 'react';
//引入connect 
import {connect} from 'dva';

class IndexPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {  };
  }
  render() {
    return (
      <div>
        <h2>我是首頁</h2>
        <h4>我是model/indexTest.js的name:{this.props.name}</h4>
      </div>
    );
  }
}

//mapStateToProps 是一個函數(函數名可以自定義),它的作用就像它的名字那樣,建立一個從(外部的)state對象到(UI組件的)props對象的映射關係。
//說白就是獲取model中定義的state
const mapStateToProps = state =>{
  //這裏可以輸出看一下,已經獲取到model的數據
  console.log(state)
  return {
    msg:'這裏是獲取model中的數據',
    name:state.indexTest.name,//獲取model/indexTest.js中的數據
  }
}

export default connect(mapStateToProps)(IndexPage);

到目前爲止我們就獲取到model中定義的數據了,那麼我要修改model中數據該怎麼樣修改呢?繼續往下看!!!(敲黑板!!!!)

4、打開src/models/indexTest.js 修改

export default {
  //命名空間
  namespace:'indexTest',
  //數據
  state:{
    name:'message'
  },
  
  //reducers:以 key/value 格式定義 reducer。用於處理同步操作,唯一可以修改 state 的地方
  reducers:{
    //修改state函數
    //state:代表上面的state,paylocad:代表組件傳過來的值
    setName(state,payLoad){
      //react中如果要修改視圖,需要創建一個新的地址纔可以重新渲染,如果只是動態修改值視圖是不會重新渲染的
      let _state = JSON.parse(JSON.stringify(state));
      _state.name = payLoad.data.name;
      //必須返回_state,不然會報錯
      return _state;
    }
  }
}

5、打開src/route/IndexPage.js 添加修改state的函數

handleSetName = () =>{
    console.log(this.props)
    //獲取到props中dispatch方法修改model的數據
    //type:配置方法名稱 type:"模塊‘namespace’/方法名"傳 要修改的值
    this.props.dispatch({
      type:'indexTest/setName',
      data:{
        name:'掏糞的小仙女'
      }
    })
}


{/* 添加修改model中state按鈕 */}
 <button onClick={this.handleSetName}>setName</button>         

這樣就實現了修改model中的state值!當然這只是 其中一種 繼續往下看,還有其他的方法。

 

四、講解model中的effects

//effects:以 key/value 格式定義 effect。用於處理異步操作和業務邏輯,不直接修改 state。由 action 觸發,可以觸發 action,可以和服務器交互,可以獲取全局 state 的數據

  //主要是用到es6中的Generator 函數

  //Generator 函數是一個普通函數,但是有兩個特徵。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不同的內部狀態(yield在英語裏的意思就是“產出”)。

  //Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。

1、打開src/models/indexTest.js 添加一個 setNameAsync函數

export default {
  //命名空間
  namespace:'indexTest',
  //數據
  state:{
    name:'message'
  },
  
  //reducers:以 key/value 格式定義 reducer。用於處理同步操作,唯一可以修改 state 的地方
  reducers:{
    //修改state函數
    //state:代表上面的state,paylocad:代表組件傳過來的值
    setName(state,payLoad){
      //react中如果要修改視圖,需要創建一個新的地址纔可以重新渲染,如果只是動態修改值視圖是不會重新渲染的
      let _state = JSON.parse(JSON.stringify(state));
      _state.name = payLoad.data.name;
      //必須返回_state,不然會報錯
      return _state;
    }
  },

  //effects:以 key/value 格式定義 effect。用於處理異步操作和業務邏輯,不直接修改 state。由 action 觸發,可以觸發 action,可以和服務器交互,可以獲取全局 state 的數據
  //主要是用到es6中的Generator 函數
  //Generator 函數是一個普通函數,但是有兩個特徵。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不同的內部狀態(yield在英語裏的意思就是“產出”)。
  //Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。
  effects:{
    //payload:頁面傳過來的值, put:理解成一個動作,通知reducers中的方法修改state
    *setNameAsync ({payload},{put,call}){
        console.log(payload)
        //調用reducers中的方法
        yield put({
          type:'setName',
          data:{
            name:payload.name
          }
        });
    }
  }

}

2、在src/route/IndexPage.js中

//使用effects  異步調用方法
  handleSetNameAsync = () =>{
    this.props.dispatch({
      type:'indexTest/setNameAsync',
      //payload需要跟model中setNameAsync方法第一個參數相同
      payload:{
        name:'豬肉佬賣西瓜'
      }
    })
  }


{/* 添加異步修改model中state按鈕 */}
<button onClick={this.handleSetNameAsync}>setNameAsync</button> 

這樣就實現了第二種修改model的方法,比較推薦這種寫法,因爲effects用於處理異步操作和業務邏輯,不直接修改 state。由 action 觸發 reducers 修改model  !!!

 

 

未完 !!! 待續!!!!

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章