新建文件夹在webstorm中打开
itcast-react
一、安装和使用yarn包管理工具
参考:yarn常用用法
推荐使用 yarn 管理 npm 依赖,并使用国内源(阿里用户使用内网源)。
配置yarn国内源:
yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global
# 国内源
$ npm i yarn tyarn -g
# 后面文档里的 yarn 换成 tyarn
$ tyarn -v
# 阿里内网源
$ tnpm i yarn @ali/yarn -g
# 后面文档里的 yarn 换成 ayarn
$ ayarn -v
tyarn 是走的国内淘宝的npm.taobao.org镜像
当我们通过yarn安装太慢的时候,可以借助tyarn来安装我们所需要的依赖
1.tyarn 安装
npm install yarn tyarn -g
2.tyarn 安装、升级、卸载和yarn的一样
//eg:
tyarn add antd
3.全局安装umi
#下面开始安装umi
tyarn global add umi
umi #进行测试
4.添加yarn安装目录到环境变量
- 如果提示 umi: command not found,你需要将 yarn global bin 路径配置到环境变量中,方法如下:
# windows系统:
# 获取 global bin 的路径
$ yarn global bin
C:\Users\Administrator\AppData\Local\Yarn\bin
# 复制上面的 global bin 的路径,添加到系统环境变量 PATH。
坑:配置完环境变量之后还是非内部或外部命令-----> 只要关闭Terminal再重新开一下就好
二、初始化测试项目目录
1.初始化yarn环境
tyarn init -y
- 此时文件夹中多出来package.json文件
2.通过umi命令创建index.js
umi g page index
- 可以看到在pages下创建好了index.js和index.css文件
3.通过命令行启动umi的后台服务,用于本地开发
umi dev
4.添加本地开发环境中umi依赖
tyarn add umi --dev
三、创建umi-dva-react开发环境
- 新建文件名为
umi-react
- 右键在webstorm中打开
tyarn init -y
tyarn add umi --dev
- 新建目录以及文件
/config/config.js
写入:
//导出一个对象,暂时设置为空对象,后面再填充内容
export default {};
- 新建目录及文件:
/src/pages/HelloWorld.js
写入:
export default () => { return <div>hello world</div>; }
tyarn add umi-plugin-react --dev
四、umi项目中开启dva进行使用
- /config/config.js中进行如下修改:
export default {
plugins:[
['umi-plugin-react',{
// 开启dva
dva:true
}]
]
};
- 创建model层管理数据
- 新建目录及文件
/src/model/DataList.js
写入:
- 新建目录及文件
export default {
namespace:'list',
state:{
data:[1,2,3],
maxNum:3
}
}
注意:在umi中,约定大于配置,约定在src/models文件夹中定义model,所以,在 该文件夹下创建ListData.js文件,通过@connect方法与view进行连接
坑:modules和models容易搞混,module(模块),model(模型),如果一不小心写错,会报错:
Could not find "store" in the context of "Connect(List)". Either wrap the ...
,名字改过来就好了,如果改名之后还不行,不要慌张,Ctrl+C终结服务,再重新umi dev
就好了
- 按需引入connect方法
import { connect } from 'dva'
- 在view中使用的时候用到ES6语法中的修饰符
- @connect函数接收两个参数,第一个参数接收一个函数,这个函数的参数为state,state是一个全局state变量,需要借助命名空间来指定对应的具体module,这个函数的作用是返回module中的数据,并且绑定到this.props上面,所以最后记得要return
// /src/pages/List.js
import React,{Component} from 'react'
// 导入dva的connect方法进行page和model的连接
import { connect } from 'dva'
// 定义一个命名空间
const namespace = 'list'
// 定义@connect函数第一参数(函数)
const mapStateToProps = state => {
// 此时的state是一个全局对象
// 必须搭配namespace下标形式映射到对应的model
// 并且可以直接获取对应model中state上的值
const listData = state[namespace].listData
const maxNum = state[namespace].maxNum
// 此函数需要return一个对象
// 该对象将直接映射到该组件的this.props上
return {
listData,
maxNum
}
}
// 通过@connect修饰符建立映射
@connect(mapStateToProps)
export default class List extends Component{
render(){
return <div>
{
this.props.listData.map((item,index) => {
return <li key={index}>{item}</li>
})
}
<button>增加</button>
</div>
}
}
- 流程说明:
- umi框架启动,会自动读取models目录下model文件,即ListData.js中的数据
- @connect修饰符的第一个参数,接收一个方法,该方法必须返回{},将接收到 model数据
- 在全局的数据中,会有很多,所以需要通过namespace进行区分,所以通过 state[namespace]进行获取数据
- 拿到model数据中的data,也就是[1, 2, 3]数据,进行包裹{}后返回
- 返回的数据,将被封装到this.props中,所以通过this.props.listData即可获取到 model中的数据
五、dva框架中修改state值的操作方法
1、首先,在model层(/models/ListData.js)中对操作方法进行定义
// 定义修改状态值的方法集合reducers
reducers:{
addNewData:function(state){ // 这里的state是指更新之前的状态数据
let maxNum = state.maxNum + 1
let newArray =[...state.listData,maxNum]
// 操作完业务逻辑之后,通过return即可更新state对象
// 类似于独立组件的this.setState()方法
return {
listData:newArray,
maxNum
}
}
}
2、在src/pages/List.js中绑定事件并且传递action
原理说明:
- @connect(参数1,参数2)
- 参数1将page层和model层进行连接,返回model中的数据,并且将数据绑定到this.props上
- 参数2将定义的函数绑定到this.props中,并且可以调用model层定义的函数
- 参数2也是一个函数,接收dispatch参数,此参数作用是调用model层定义的函数,有返回值,返回值是一个对象,其中的函数可以被this.props调用
- 参数2函数的返回值对象的函数中,通过调用dispatch方法,传入对象即可调用model中定义的方法,所传入对象的第一个值格式为
type:namespace + '/model中函数名'
代码如下:
// 定义@connect函数的第二个参数(函数)
onst mapDispatchToProps = dispatch => { // 需要接收dispatch作为参数
return { // 返回一个装满函数的对象,直接绑定到this.props上
add : function () {
dispatch({ // 在返回的函数内部调用dispatch方法,参数为一个对象
type:namespace + '/addNewData' // 对象第一个值为type,格式为:namespace + '/model中的方法名'
})
}
}
/ 通过@connect修饰符建立映射
connect(mapStateToProps,mapDispatchToProps)
// 绑定事件:
<button onClick={this.props.add}>增加</button>
坑:dispatch哪来的?----->内置函数
六、dva框架model中请求数据
- 创建目录以及文件
/src/Utils/request.js
写入内容(固定写法):
// /src/util/request.js
// import fetch from 'dva/fetch';
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 async function request(url, options) {
const response = await fetch(url, options);
checkStatus(response);
return await response.json();
}
- model文件中,和
namespace
,state
,reducers
平级,新增effects配置,用于异步请求加载数据
// 增加effects配置用于进行异步数据请求
effects: {
*initData(params, sagaEffects) { //定义异步方法---->generator函数
const {call, put} = sagaEffects; // 需要传入sagaEffects以获取到call、put方法
const url = "/ds/list"; // 定义请求的url
let data = yield call(request, url); //执行请求,call方法需要request作为参数
yield put({ // 调用reducers中的方法并且传入对象作为参数,对象中有type和data两个属性
type : "addNewData", //指定调用方法名
data : data //ajax请求传递回来的数据
});
}
}
- 由于call方法需要request作为参数,所以在头部引入
import request from '../util/request';
- 调用时机:
ComponentDidMount
componentDidMount(){
this.props.init ()
}
- mapDispatchToProps中建立映射
return的对象中新增:
init : () => {
dispatch({
type:namespace + '/initData'
})
}
七. 处理请求的url地址
由于url = "/ds/list"
不存在所以会报错
- mock模拟请求服务器
- 新建目录及文件
/src/mock/MockListData.js
写入:
- 新建目录及文件
export default {
'get /ds/list': function (req, res) { //模拟请求返回数据
res.json({
data: [1, 2, 3, 4, 5],
maxNum: 5
});
},
}
坑:一定要重启服务才能正确请求到!
-
注意请求回来的数据值类型,需要和state类型一致
-
reducers中被触发的函数的第二个参数为effects中函数传来值,其上的result.data和state类型一致,可以直接return并修改sate
八.项目中使用antd
-
umi+dva中引入antd
在 umi 中,你可以通过在插件集 umi-plugin-react 中配置 antd 打开 antd 插件,antd 插件会帮你引入 antd 并实现按需编译。
- 新建文件用于演示
/src/pages/MyTabs.js
坑:在config.js中
antd:true
之后仍然要重启服务
ES6对象的结构赋值特性:
- 通过同一对象中属性扩散可覆盖属性值
const state = {
num:0
}
let num = 2
// 将state对象结构,num实际为num = num,覆盖之前state解构出来的属性值
console.log({...state,num}) // {num:2}
const state1 = {
num1:0,
a:21,
b:12
}
let [num1,a,b] = [23,54,67]
console.log({...state1}) // {num1: 0, a: 21, b: 12}
const state3 = {
x:0,
y:21,
z:12
}
let [x,y,z] = [23,54,67]
console.log({...state3,y,z}) // {x: 0, y: 54, z: 67}
- 区分变量和模式
变量必须与对象的属性同名,才能取到正确的值,如果没有对应的同名属性,取不到值的话,值为undefined
var {bar, foo} = {foo:'aaa',bar:'bbb'}//foo的值是'aaa',bar的值是'bbb'
//注意真正被赋值的是谁
var {foo:baz} = {foo:"aaa",bar:"bbb"}//baz的值是"aaa"而不是foo的值是“aaa”,foo是个“模式”
谁后面有“:”,谁就是模式,并不是变量
- 从对象的多个属性中只继承一个属性
const state = {
size: 'large',
icon:'download'
};
let { size } = state
console.log( value )
扩展资料: