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