虽然我们可以直接安装dva脚手架,但是安装的dva脚手架和我们原来的react脚手架的目录结构还是不太一样的,这让我们有些小伙伴用的不太顺手,如果有需要在react中脚手架手搭dva的小伙伴,本文章应该可以帮到您
首先我们先安装react-cli:
npx create-react-app myapp
其次在安装dva-core(内部已提供了redux包):
npm i dva-core
最后需要在下载react-redux包(因为dva-core这个包并没有内置react-redux;因为版本的问题,我安装的是@7.2.6版本的):
npm i react-redux@7.2.6
好啦,基本的包咱们已经安装完毕了,开始搭建store吧!
1、创建store文件夹/index.js
import {create} from 'dva-core'//引入create
import models from './models'//引入模块:[]
import loading from 'dva-loading'//在开发异步加载的功能时,为提高用户体验一般会显示加载提示
const app = create()//创建应用实例
if(Array.isArray(models)) {//判断是否为数组
models.forEach(model => {//遍历每一项
app.model(model)//使用模块
})
}
app.use(loading()) //注册插件
app.start()//应用开启
export default app._store;//暴露store
2.1、在store目录下创建models文件夹/login.js
import axios from 'axios//下载一下axios 引入
const asyncGetCode = (url, params) => (url, params) => {//封装get请求方法
return new Promise((resolve) => {
axios.get(url, {params}).then(resp => {
resolve(resp)
})
})
}
const asyncLogin = (url, params) => {//封装post请求方法
return new Promise((resolve) => {
axios.post(url, params).then(resp => {
resolve(resp)
})
})
}
export default {
namespace: 'login',//类似于vuex的命名空间
state: {//定义初始状态
token: localStorage.getItem('token') || '',
codeId: ''
},
reducers: {//定义更新状态的方法
setToken(state, {data}) {
return Object.assign({}, state, data)
},
setCodeId (state, {data}) {
return Object.assign({}, state, data)
}
},
effects: {//定义副作用方法 (异步) * 使用了 ES6 的 Generator 功能,让异步的流程更易于读取,写入和测试
*getCodes(payload, {call, put}) {//获取验证码方法
let res = yield call(asyncGetCode, '/api/mobile/getCode') //用内置参数call调用获取验证码接口
if(res.data.code === 200) {
alert('验证码 =====> ' + res.data.data.text) //打印出来
yield put({type: 'setCodeId', data: {codeId: res.data.data.codeId}}) //用内置put分发action对象更改状态中的codeId
}else {
console.log(res.data.msg)
}
},
*login(payload, {call, put, select}) {
let state = yield select() //通过内置参数select获取状态state
let loginInfo = Object.assign({}, payload.data, {codeId: state.login.codeId})//合并登陆字段
let loginRes = yield call(asyncLogin, '/api/mobile/login', loginInfo) //用内置参数call调用登录接口
if(loginRes.data.code === 200) {
localStorage.setItem('token', loginRes.data.token) //存本地token
yield put({type: 'setToken', data: {token: loginRes.data.token}})// 用内置参数put分发action 更新状态中的token
}else {
console.log(loginRes.data.msg)
}
},
}
};
2.2、再创建models文件夹/home.js(创建这个文件虽然没啥作用,但主要是为了演示dva怎样实现模块化)
import axios from 'axios'
const getHomeList = (url, params) => {//封装post请求方法
return new Promise((resolve) => {
axios.post(url, params).then(resp => {
resolve(resp)
})
})
}
export default {
namespace: 'home',
state: {
referList: []
},
reducers: {
SETREFERLIST(state, {data}) {
return Object.assign({}, state, data)
},
},
effects: {
*asyncGetList (payload, {call, put}) {
const resp = yield call(getHomeList, '/api/admin/getRefer')//调用方法
if(resp.data.code === 200) {
yield put({type: 'SETREFERLIST', data: {referList: resp.data.data}})//更改状态
}
},
}
};
2.3、最后创建models文件夹/index.js(集中管理所有模块,最后抛出在store/index.js中接收)
import home from './home'//引入home模块
import login from './login'//引入login模块
const models = [home, login]
export default models //暴露所有模块 详见标题1
3、正常在脚手架入口文件index.js中引入store,并注入组件
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import store from './store'
import App from './App'
const container = document.getElementById('root')
ReactDOM.render(
<Provider store = {store} >
<App />
</Provider>,
container
)
4、在home组件中应用store(开发用的是类组件所以借助了connect高阶组件,函数组件的话还是那两hooks)
import React, { Component } from 'react';
import { connect } from 'react-redux'//引入connect
class Home extends Component {
componentDidMount() {//在组件挂载完毕的钩子中调用redux 的异步方法
this.props.asyncGetList()
}
render() {
const {referList} = this.props.state//解构
return (
<div className="home-container">
{referList.length > 0 && referList.map(item => {
return (
<dl key={item.id}>
<dt>
<img src={item.referimg} />
</dt>
<dd>
<b>
{item.title}
</b>
<p>
<span>{item.username}</span>
<span>{item.createtime}</span>
</p>
</dd>
</dl>
)
})}
</div>
)
}
}
export default connect(//映射状态和方法
(state) => ({state: state['home']}),
{
asyncGetList: () => ({type: 'home/asyncGetList'})
}
)(Home);
4.1、在login组件中应用store
(这段代码有些地方和本文初衷有些相背,重点看login和getCode的代码即可,主要还是看dva如何实现redux和组件间如何交互的。借助了antd-mobile组件库实现的登录功能,感兴趣的可以安装一下
npm i antd-mobile
import React, { Component } from 'react'
import { Form, Button, Input, Dialog } from 'antd-mobile'
import { EyeInvisibleOutline, EyeOutline } from 'antd-mobile-icons'
import { connect } from 'react-redux'
class index extends Component<any> {
state = {//控制弹框的显示隐藏的状态
visible: false,
}
getCode() {//获取验证码的方法 *重要
const { getCode } = this.props
getCode()
}
selfMotionLogin() { //自动登录
const { token } = this.props.state['login']
if (token) {
this.props.history.push('/layout/home')
}
}
login(values) { //登录方法 *重要
const { login } = this.props
login(values)
}
shouldComponentUpdate(nextProps) {
if (nextProps.state['login'].token) {
this.props.history.push('/layout/home')
}
return true
}
componentWillMount() {
this.selfMotionLogin()
}
render() {
const { visible } = this.state
return (
<div className="login-container">
<Form
layout='horizontal'
name='form'
onFinish={(e) => this.login(e)}
footer={
<Button block type='submit' color='primary' size='large'>
登陆
</Button>
}>
<Form.Item label='用户名' name='username'>
<Input placeholder='请输入用户名' clearable />
</Form.Item>
<Form.Item
label='密码'
name='password'
extra={
<div className='eye'>
{!visible ? (
<EyeInvisibleOutline onClick={() => this.setState({ visible: true })} />
) : (
<EyeOutline onClick={() => this.setState({ visible: false })} />
)}
</div>
}
>
<Input
placeholder='请输入密码'
clearable
type={visible ? 'text' : 'password'}
/>
</Form.Item>
<Form.Item
label='短信验证码'
name='code'
extra={
<div className='horizontal'>
<a onClick={() => this.getCode()}>发送验证码</a>
</div>
}
>
<Input placeholder='请输入验证码' clearable />
</Form.Item>
</Form>
{/*借助dva-loading实现的登陆时加载功能*/}
<Dialog
visible={this.props.state.loading.effects['login/login']}
content='~~~~~ loading ~~~~~'
closeOnAction
/>
</div>
)
}
}
export default connect(//映射状态和方法
(state) => ({ state: state }),
{
login: (payload) => ({ type: 'login/login', data: payload }),//登陆
getCode: () => ({ type: 'login/getCodes' })//获取验证码
}
)(index)
总结:搭建的过程可能还是些许繁琐,但是也可以看到用的时候还是很惬意的,和vue的vuex对比也是不遑多让了。 对了,最后介绍一下dva吧:它首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。(来自dva官网,详情见dva官网) dva是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易