DVA简介
之前用CRA这个脚手架进行react项目开发,react-router-dom / redux / react-redux / redux-thunk等
什么是DVA:就是一个轻量级的数据流框架 (主要是redux、redux-saga 也有路由、异步请求模块等)
DVA安装
步骤1:安装脚手架dva-cli
npm i dva-cli -g
或
yarn add global dva-cli
步骤2:验证是否安装成功
dva -v
DVA创建项目
步骤1:通过脚手架工具生成dva框架代码
dva new dvashop
步骤2:启动测试
cd dvashop
yarn start
或者
npm start
DVA路由
创建login路由
步骤1:定义路由 src/router.js文件
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import LoginPage from './routes/LoginPage';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<!--配置路由,将路由与视图组件进行映射-->
<Route path="/login" exact component={LoginPage} />
</Switch>
</Router>
);
}
export default RouterConfig;
步骤2:创建组件 src/routes目录下创建
import React, { Component } from 'react'
class LoginPage extends Component {
render() {
return (
<div>
<h1>this is Login</h1>
</div>
)
}
}
export default LoginPage
声明式导航
import {NavLink as Link} from "dva/router"
{/* 通过声明式导航的方式实现路由跳转 */}
<p><Link to="/login">进入登录页面</Link></p>
编程式导航
<p><button onClick={this.back}>回到首页</button></p>
back = ()=>{
//通过编程式导航的方式实现路由跳转
this.props.history.push("/")
}
因为LoginPage是路由组件,所以它的属性上面就会有路由相关的api。
但是如果某个组件不是路由组件?需要通过引入高阶组件withRouter,将普通组件变成伪路由组件,这样的话,上面也会有路由相关的api属性了。
(HOC高阶组件: 本质上就是一个函数,接受一个组件,最终返回一个新组件,可以给新组件上面传递额外的属性。)
connect()(UIComponent)
React.memo(functional component)
withRouter(Component)
切换路由模式
实现
步骤1:安装
yarn add history
步骤2:修改src/index.js 入口文件
import {createBrowserHistory as createHistory} from 'history'; const app = dva({ history: createHistory(), });
DVA模型models
定义与激活models
需求:定义购物车路由 它的模型里面定义redux的状态 num:666
实现
a.定义cars数据模型 src/models/cars
export default { //定义命名空间,后续取redux状态的时候,通过此命名空间取值 namespace: 'cars', state: { num:666 }, }
b. 激活cars数据模型
import Cars from "./models/cars" app.model(Cars)
使用models
直接使用connect高阶组件
原理:react-redux被dva封装 从这里面导出connect
代码
import React, { Component } from 'react'
import { connect } from 'dva'; // connect其实就是dva封装了react-redux里面的
class CarsPage extends Component {
render() {
return (
<div>
<h1>this is Cars</h1>
<h1>购物车( {this.props.num} )</h1>
</div>
)
}
}
const mapStateToProps = state => {
console.log('在cars组件中打印状态树数据', state)
return {
num: state.cars.num
}
}
// export default CarsPage
export default connect(mapStateToProps)(CarsPage)
更新models
步骤1:修改redux状态,需要单独定义reducers
export default {
// 一定要留心因为dispatch触发就是根据它
// 命名空间
namespace: 'cars',
// 状态数据
state: {
num: 666
},
// action更新state
reducers: {
// 函数(state, action) {}
// 深拷贝
setNum(state, action) {
// 深拷贝
let _state = JSON.parse(JSON.stringify(state))
// 更新(默认的值+传递过来的值
_state.num += action.payload.num
// 返回
return _state
}
},
}
步骤2:触发reducers中的方法
import React, { Component } from 'react'
import { connect } from 'dva'; // connect其实就是dva封装了react-redux里面的
class CarsPage extends Component {
render() {
return (
<div>
<h1>this is Cars</h1>
<h1>购物车( {this.props.num} )</h1>
<button onClick={()=>{
this.props.dispatch({ //通过dispatch派发action对象给reducer进行处理
// 命名空间/reducers名字
type: 'cars/setNum',
payload: {num:1}
})
}}>更新+1</button>
<button onClick={()=>{
this.props.dispatch({
// 命名空间/reducers名字
type: 'cars/setNum',
payload: {num:2}
})
}}>更新+2</button>
</div>
)
}
}
const mapStateToProps = state => {
console.log('在carts组件中打印状态树数据', state)
return {
num: state.carts.num
}
}
// export default CartsPage
export default connect(mapStateToProps)(CartsPage)
DVA模型effects
明确概念
手册:https://dvajs.com/knowledgemap/#effect
effects 异步处理
put 用户触发action 给reducer处理
call 用于调用异步函数
练习
effects 就是异步请求 拿到数据之后,通过 reducers来更新
内部安装了redux-saga , 要求我们进行异步操作的函数放入到effects中进行定义,都是generator形式的函数。
1 触发effects 通过console.log输出就行
effects: {
//es6中的generator函数 (async await)
*demoAsync({ payload }, { call, put }) { // eslint-disable-line
yield console.log("触发了demoAsync这个异步方法...",payload)
},
},
<p><button onClick={()=>{
this.props.dispatch({
type:"cars/demoAsync",
payload:{
a:100
}
})
}}>触发demoAsync异步方法</button></p>
2 触发effects 更新num (暂时不用异步请求
effects: {
//es6中的generator函数 (async await)
//call是可以进行异步调用
//put是用于触发 action , 更改redux状态
*demoAsync({ payload }, { call, put }) { // eslint-disable-line
yield put({type:"setNum",payload:{num:payload.a}})
},
},
3 触发effects 异步请求(这是咱们最终想完成的需求
state: {
num:666,
films:[]
},
reducers: {
setFilms(state,action){
return {
...state,
films:action.payload.films
}
}
},
effects: {
//es6中的generator函数 (async await)
//call是可以进行异步调用
//put是用于触发 action , 更改redux状态
*setFilmsAsync({ payload }, { call, put }){
//call异步请求获取数据
let data = yield call(getFilmsData);
//put去触发action提交给reducer,从而实现更改redux状态
yield put({type:"setFilms",payload:{films:data.data.data.films}})
}
},
service/cars.js
import request from '../utils/request';
export function getFilmsData() {
return request('https://m.maizuo.com/gateway',{
headers:{
'X-Client-Info': '{}',
'X-Host': 'mall.film-ticket.film.list'
}
});
}
CarsPage
const mapState = state=>{
return {
num:state.cars.num,
films:state.cars.films
}
}
DVA模型subscriptions
models/login
export default {
namespace: 'login',
state:{},
reducers:{},
subscriptions: {
//初始化的时候只要进入登录页面就会触发cars/setFilmsAsync
setup({ dispatch, history }) {
history.listen(({ pathname }) => {
if (pathname === '/login') {
dispatch({
type: 'cars/setFilmsAsync',
});
}
});
},
},
}
app.model(require('./models/login').default);
DVA周边roadhog
简介
是什么:是一个命令行工具,相当于dva版的webpack(相对比较简单
能干嘛:配置多页面、CSS预处理、前端代理等操作
去哪下:不用下,因为集成在dva里面
仓库地址:https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md
配置(单入口分析
默认:打包index.js -> public/index.html自定引入(原理就是咱们昨天讲的HTML插件
现在:实现多入口 增加about.js通过about.html可以访问其内容
实现:
步骤1:创建src/about.js
// console.log('hello,webopenfather') import React, { Component } from 'react' import {render} from 'react-dom' class About extends Component { render() { return ( <div> <h1>this is about page</h1> </div> ) } } render(<About />, document.querySelector('#root'))
步骤2:创建about.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>测试多入口About</title> <link rel="stylesheet" href="index.css" /> </head> <body> <div id="root"></div> <script src="about.js"></script> </body> </html>
步骤3:.webpackrc配置多入口
"entry": { "index": "./src/index.js", "about": "./src/about.js" }
配置(CSS模块化
明确:roadhog可以简单简单理解为基于webpack封装
然后:和cra一样css、less都默认直接可以使用
import stylesLess from './LoginPage.less';
<div className={`${stylesLess.main} ${styles.aaa}`}>
配置(跨域
练习:
发现跨域报错
解决
export function getDoubanData(){
return request("http://47.96.0.211:9000/db/in_theaters")
}
effects:{
*getDataAsync({payload},{call,put}){
let data = yield call(getDoubanData)
console.log("data===>",data)
}
}
<p><button onClick={()=>{
this.props.dispatch({
type:"login/getDataAsync"
})
}}>请求getDataAsync</button></p>
export default connect()(LoginPage)
webpackrc配置文件配置代理:
"proxy": {
"/api": {
"target": "http://47.96.0.211:9000",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
}