Dva教程
2019千锋DvaJS视频精讲,全网首发!【Web前端】
千锋Web前端教程:01_dva_初始化
(01_dva_初始化&)
01_dva_初始化&
暴露request对象发送请求
其他地方调用query
src\routes\IndexPage.jsx
import React from 'react';
import {connect} from "dva";
import * as apis from "../services/example"
class IndexPage extends React.Component{
handleSetName=()=>{
this.props.dispatch({
type:"indexTest/setName",
data:{
name:"猪猪侠"
}
})
}
handleSetNameAsync=()=>{
this.props.dispatch({
type:"indexTest/setNamaAsync",
data:{
name:"猪猪侠"
}
})
}
componentDidMount(){
// apis.testCnode().then((res)=>{
// console.log(res);
// })
// 测试mock
apis.mockdata().then((res)=>{
console.log(res);
})
}
testCnode=()=>{
this.props.dispatch({
type:"indexTest/testCnode"
})
}
render(){
console.log(this.props.cnodeData);
return (
<div>
我是首页
{this.props.msg}
<div>
{
this.props.name
}
</div>
<button onClick={this.handleSetName}>setName</button>
<button onClick={this.handleSetNameAsync}>setNameAsync</button>
<button onClick={this.testCnode}>testCnode</button>
</div>
)
}
}
const mapStateToProps=state=>{
return {
msg:"我爱北京天安门",
name:state.indexTest.name ,
cnodeData:state.indexTest.cnodeData
}
}
export default connect(mapStateToProps)(IndexPage);
src\utils\request.js
import fetch from 'dva/fetch';
function parseJSON(response) {
return response.json();
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => ({ data }))
.catch(err => ({ err }));
}
src\services\example.js
import request from '../utils/request';
const pox="/apis"
export function query() {
return request('/api/users');
}
export function testCnode() {
return request(pox+'/api/v1/topics');
}
// 注册mock接口
export function mockdata() {
return request("api/mockdta");
}
千锋Web前端教程:02_dva_路由
(02_dva_路由&)
02_dva_路由&
增加路由
增加userPage
Dva的初始化
进行跳转,包裹组件
使用按钮跳转
通过history跳转
Routes放在路由中配置的组件
Component放入通用组件
引入通用组件
Child中进行跳转,props中没有history
通过withRoutes写入路由
另一种跳转方式
修改首页,#为/路由
起别名,减少报错
src\routes\userPage.jsx
import React ,{Fragment}from 'react';
import {Link} from 'dva/router';
import Child from "../components/child"
class userPage extends React.Component{
handleToIndex=()=>{
// console.log(this.props)
this.props.history.push("/")
}
render(){
return (
<Fragment>
<div>我是用户页</div>
<Link to="/">首页</Link>
<button onClick={this.handleToIndex}>首页</button>
<Child/>
</Fragment>
)
}
}
export default userPage
src\router.js
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import userPage from './routes/userPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/user" exact component={userPage} />
</Switch>
</Router>
);
}
export default RouterConfig;
src\components\child.jsx
import React from 'react';
import { withRouter } from 'dva/router';
class Child extends React.Component {
handleToIndex(){
// console.log(this.props)
this.props.history.push("/");
}
render() {
return (
<div>
<div>我是通用组件</div>
<button onClick={this.handleToIndex.bind(this)}>首页_child</button>
</div>
)
}
}
export default withRouter(Child);
userPage.jsx
import React ,{Fragment}from 'react';
import {Link} from 'dva/router';
import Child from "../components/child"
class userPage extends React.Component{
handleToIndex=()=>{
// console.log(this.props)
this.props.history.push("/")
}
render(){
return (
<Fragment>
<div>我是用户页</div>
<Link to="/">首页</Link>
<button onClick={this.handleToIndex}>首页</button>
<Child/>
</Fragment>
)
}
}
export default userPage
src/index.js
import dva from 'dva';
import './index.css';
// 1. Initialize
import {createBrowserHistory as createHistory} from 'history';
const app = dva({
history: createHistory(),
});
// const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
// app.model(require('./models/example').default);
// 引入models
app.model(require('./models/indexTest').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
千锋Web前端教程:03_models_reducers
(03_models_reducers&)
03_models_reducers&
多个model数据仓库,都要有namespace
引用注册models
视图中使用model
读取model中的state,用法和reducers一样
定义多个state
获取name Misa
要可以修改name值,setName第一个参数是state,第二个参数是传入的值playload,代表从上面的state,playload从组建中传递过来的参数,要返回state
调用setName,点击按钮
通过dispatch进行调用,type通过namespace进行调用后面是要调用的函数
点击调用函数
传递参数
点击获取值
点击视图没有更新,不要生成新的state,生成新的地址
src\models\indexTest.js
import * as apis from "../services/example"
export default {
namespace: 'indexTest',
state: {
name:"Msea",
cnodeData:[]
},
reducers:{
setName(state,payLoad){
// console.log(payLoad.data.name);
let _state=JSON.parse(JSON.stringify(state));
_state.name=payLoad.data.name;
return _state;
},
setCnodeDataList(state,payLoad){
let _state=JSON.parse(JSON.stringify(state));
_state.cnodeData=payLoad.data
return _state;
},
testPath(state,payLoad){
console.log("用户页");
return state;
}
},
effects:{
*setNamaAsync ({payload},{put,call}){
yield put({
type:"setName",
data:{
name:"超人强"
}
})
},
*testCnode({payload},{put,call}){
let rel= yield call(apis.testCnode);
if(rel.data){
yield put({
type:"setCnodeDataList",
data:rel.data.data
})
}
}
},
subscriptions:{
haha({dispatch,history}){
history.listen(({pathname})=>{
if(pathname==="/user"){
dispatch({
type:"testPath"
})
}
})
}
}
};
index.js
import dva from 'dva';
import './index.css';
// 1. Initialize
import {createBrowserHistory as createHistory} from 'history';
const app = dva({
history: createHistory(),
});
// const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
// app.model(require('./models/example').default);
// 引入models
app.model(require('./models/indexTest').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
IndexPage.jsx
import React from 'react';
import {connect} from "dva";
import * as apis from "../services/example"
class IndexPage extends React.Component{
handleSetName=()=>{
this.props.dispatch({
type:"indexTest/setName",
data:{
name:"猪猪侠"
}
})
}
handleSetNameAsync=()=>{
this.props.dispatch({
type:"indexTest/setNamaAsync",
data:{
name:"猪猪侠"
}
})
}
componentDidMount(){
// apis.testCnode().then((res)=>{
// console.log(res);
// })
// 测试mock
apis.mockdata().then((res)=>{
console.log(res);
})
}
testCnode=()=>{
this.props.dispatch({
type:"indexTest/testCnode"
})
}
render(){
console.log(this.props.cnodeData);
return (
<div>
我是首页
{this.props.msg}
<div>
{
this.props.name
}
</div>
<button onClick={this.handleSetName}>setName</button>
<button onClick={this.handleSetNameAsync}>setNameAsync</button>
<button onClick={this.testCnode}>testCnode</button>
</div>
)
}
}
const mapStateToProps=state=>{
return {
msg:"我爱北京天安门",
name:state.indexTest.name ,
cnodeData:state.indexTest.cnodeData
}
}
export default connect(mapStateToProps)(IndexPage);
router.js
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import userPage from './routes/userPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/user" exact component={userPage} />
</Switch>
</Router>
);
}
export default RouterConfig;
千锋Web前端教程:04_models_effects
(04_models_effects&)
04_models_effects&
Model中的异步,基于redux的saga
Generator函数前面都要有*,第一个参数是payload,第二个参数是put和call
Yield是console的结果
目的要修改state,异步执行调用接口,获取数据放到state就可以全局使用了
尽量不要再reducers写逻辑
Put传递一个action,需要一个type,直接调用setName就可以,和传入的参数
调用put,yield调用一个action。通过yield调用put,put里面发action调用方法,call就是发送异步
异步函数调用也需要触发一下
千锋Web前端教程:05_models_Api
(05_models_Api&)
05_models_Api&
Dva的异步,通过接口,通过代理调用接口,代理在webpackrc中
去掉后面的path,代理改变重启服务
部署接口通过servers,通过发送ajax的对象调用
Fetch发送的ajax
暴露一个接口
需要代理,发现请求路径由apis会替换url
pages调用接口
测试,初始化生命周期,初始化运行触发testCode(),返回promise获得接口返回的数据
数据遍历到视图中
在models部署一个接口取数据
Call中引入接口,第二个参数要要带的参数
调用接口事件触发
调用发送请求
最终的目的要赋值给state,在视图中取到数据
Call发送接口要通过service拿到方法获取数据,异步判断什么时候拿到值,Yield put()发送一个action调用reducers,在reducer中定义方法
变为字符串,state中从playload中取里面的,要返回state不然报错。
到pages中去取值,state中取值,通过namespace和方法名
处理异步if()或者this.props
数据回来了render会执行一次并且打印console.log
Map遍历,掉接口绑定异步,接口中取值
首先配置代理访问接口,/api路径里有api就要做代理,services中传接口定义方法,pages中调用service中的方法,在pages中可以通过api调用方法promise中取数据。
Models中定义和调用,使用call发送异步请求,数据回来通过put触发actions type,在actions中直接修改了state变量。数据修改之后在mapStateToProps中的state中获取数据
通过连接获取
.webpackrc
{
"proxy": {
"/apis": {
"target": "https://cnodejs.org",
"changeOrigin": true,
"pathRewrite": { "^/apis" : "" }
}
}
}
\src\services\example.js
import request from '../utils/request';
const pox="/apis"
export function query() {
return request('/api/users');
}
export function testCnode() {
return request(pox+'/api/v1/topics');
}
// 注册mock接口
export function mockdata() {
return request("api/mockdta");
}
request.js
import fetch from 'dva/fetch';
function parseJSON(response) {
return response.json();
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => ({ data }))
.catch(err => ({ err }));
}
IndexPage.jsx
点击testCode数据回来以后会执行一次render,点击会再执行一次
import React from 'react';
import {connect} from "dva";
import * as apis from "../services/example"
class IndexPage extends React.Component{
handleSetName=()=>{
this.props.dispatch({
type:"indexTest/setName",
data:{
name:"猪猪侠"
}
})
}
handleSetNameAsync=()=>{
this.props.dispatch({
type:"indexTest/setNamaAsync",
data:{
name:"猪猪侠"
}
})
}
componentDidMount(){
// apis.testCnode().then((res)=>{
// console.log(res);
// })
// 测试mock
apis.mockdata().then((res)=>{
console.log(res);
})
}
testCnode=()=>{
this.props.dispatch({
type:"indexTest/testCnode"
})
}
render(){
console.log(this.props.cnodeData);
return (
<div>
我是首页
{this.props.msg}
<div>
{
this.props.name
}
</div>
<button onClick={this.handleSetName}>setName</button>
<button onClick={this.handleSetNameAsync}>setNameAsync</button>
<button onClick={this.testCnode}>testCnode</button>
</div>
)
}
}
const mapStateToProps=state=>{
return {
msg:"我爱北京天安门",
name:state.indexTest.name ,
cnodeData:state.indexTest.cnodeData
}
}
export default connect(mapStateToProps)(IndexPage);
indexTest.js
import * as apis from "../services/example"
export default {
namespace: 'indexTest',
state: {
name:"Msea",
cnodeData:[]
},
reducers:{
setName(state,payLoad){
// console.log(payLoad.data.name);
let _state=JSON.parse(JSON.stringify(state));
_state.name=payLoad.data.name;
return _state;
},
setCnodeDataList(state,payLoad){
let _state=JSON.parse(JSON.stringify(state));
_state.cnodeData=payLoad.data
return _state;
},
testPath(state,payLoad){
console.log("用户页");
return state;
}
},
effects:{
*setNamaAsync ({payload},{put,call}){
yield put({
type:"setName",
data:{
name:"超人强"
}
})
},
*testCnode({payload},{put,call}){
let rel= yield call(apis.testCnode);
if(rel.data){
yield put({
type:"setCnodeDataList",
data:rel.data.data
})
}
}
},
subscriptions:{
haha({dispatch,history}){
history.listen(({pathname})=>{
if(pathname==="/user"){
dispatch({
type:"testPath"
})
}
})
}
}
};
千锋Web前端教程:06_models_subscriptions
(06_models_subscriptions&)
06_models_subscriptions&
Subscription订阅消息,dispatch调用方法和history,初始化就会注册函数
监听路径如果user触发,如果首页不触发history.listen,判断pathname
用户页修改数据,dispatch调用testPath函数
监听一直都在
千锋Web前端教程:07_dva.mock
(07_dva.mock&)
07_dva.mock&
Mock文件夹下模拟接口,不是代理方式,制作mock数据,返回json数据
调用mock,在roadhogic中暴露接口,拿到js文件
已经合法是一个mock接口
在service中注册mock接口 ,不需要proxy
Pages视图调用方法,发送接口,
也可以在effect通过异步call调用发送异步请求和put执行action
Reducers不要写逻辑,修改state状态
注册监听,路由发生dispatch触发reducers中的方法
mock\testMock.js
与代理没有关系,req请求,res响应,响应json登录成功
module.exports={
"GET /api/mockdta":(req,res)=>{
console.log(req);
res.send({
msg:"登录成功"
})
}
}
.roadhogrc.mock.js
注册上进行调用
export default {
...require("./mock/testMock")
};
example.js
import request from '../utils/request';
const pox="/apis"
export function query() {
return request('/api/users');
}
export function testCnode() {
return request(pox+'/api/v1/topics');
}
// 注册mock接口
export function mockdata() {
return request("api/mockdta");
}
IndexPage.jsx
import React from 'react';
import {connect} from "dva";
import * as apis from "../services/example"
class IndexPage extends React.Component{
handleSetName=()=>{
this.props.dispatch({
type:"indexTest/setName",
data:{
name:"猪猪侠"
}
})
}
handleSetNameAsync=()=>{
this.props.dispatch({
type:"indexTest/setNamaAsync",
data:{
name:"猪猪侠"
}
})
}
componentDidMount(){
// apis.testCnode().then((res)=>{
// console.log(res);
// })
// 测试mock
apis.mockdata().then((res)=>{
console.log(res);
})
}
testCnode=()=>{
this.props.dispatch({
type:"indexTest/testCnode"
})
}
render(){
console.log(this.props.cnodeData);
return (
<div>
我是首页
{this.props.msg}
<div>
{
this.props.name
}
</div>
<button onClick={this.handleSetName}>setName</button>
<button onClick={this.handleSetNameAsync}>setNameAsync</button>
<button onClick={this.testCnode}>testCnode</button>
</div>
)
}
}
const mapStateToProps=state=>{
return {
msg:"我爱北京天安门",
name:state.indexTest.name ,
cnodeData:state.indexTest.cnodeData
}
}
export default connect(mapStateToProps)(IndexPage);