一、初始化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 !!!
未完 !!! 待續!!!!