1 从 Vue 技术栈转到 React 技术栈的历程
大家好,我是 Jone,有两年的前端开发经验,19 年在某鸟培训机构学习,学习了HTML CSS JS三件套的一点点皮毛,再加上Vue2的技术栈,就出来找工作了,毕业之后又刚好赶上疫情,通过视频面试的方式,艰难的找到了第一份工作,公司用的是Vue2+ElementUI开发,工作一年的时间,也慢慢掌握了公司的业务,做的也就是一些表单表格,没有任何提升的空间,想突破一下自己,就主动提出离职了。第二年经过同事的内推,进了一家使用Vue3+Vite+Antd技术栈的公司,当时Vue3刚出来不久,也算是第一个吃螃蟹的了哈哈哈,后来因为公司经营问题倒闭了,不得不又开始找工作,工资至今未发。今年来到大上海,重新开始找工作之路,目前这一家公司的技术栈是React Hooks+TypeScript+微前端qiankun+Umi+Dva+Antd刚开始对于一个从没接触过React技术栈的我来说十分陌生,也走过不少弯路,不过作为一个有着一年多的开发经验Vueer,还是有一点组件化 函数式编程的基础,这两个技术栈之间也有许多相似之处,后来经过一两个月的摸索,从一开始进公司的看不懂代码,终于到现在可以掌握正常的开发业务。
2 脚手架的路由差异
之前 Before
之前Vue用的脚手架是Vue Cli搭建的脚手架,路由也是用的Vue的生态Vue Router
之后 After
现在用的是Umi搭建的脚手架,中文发音乌米,和Vue技术栈不同的是Umi集成了react react-router 开箱即用。同时还内置了请求库Umi request、数据管理dva等。
3 数据及状态管理的差异
之前 Before
我们在Vue 技术栈时通常使用axios二次封装,并且在src目录下新建一个api的文件夹来对我们系统的每个模块的接口进行统一管理。
src
|-- components
|-- api 请求接口统一管理
在页面中导入 api 的接口,在methods里面定义一个方法,在mounted生命周期函数里调用。
import {getTableList} from '@/api/policySystem'
export default {
data() {
return {
tableData: []
}
},
mounted() {
this.getTableList()
},
methods:{
async getTableList(){
const data = {
'strMap.taskid':'203537773243756544',
}
const res = await getTableList(data)
if (res.success) {
this.tableData = res.data.zctx
}
}
}
}
获取到接口数据后再对 data 进行赋值,data 里的数据改变,触发视图的更新渲染。
之后 After
现在数据请求用的是Umi-request,Umi-request 是基于 fetch 封装的开源 http 请求库,统一的 API 调用方式,同时简化使用方式,提供了请求层常用的功能
- URL 参数自动序列化
- POST 数据提交方式简化
- Response 返回处理简化
- 请求超时处理
- 请求缓存支持
- GBK 编码处理
- 统一的错误处理方式
- 请求取消支持
- Node 环境 http 请求
- 拦截器机制
- 洋葱中间件机制
| 特性 | umi-Request | fetch | axios |
|---|---|---|---|
| query简化 | √ | × | √ |
| post简化 | √ | × | × |
| 请求超时 | √ | × | √ |
| 请求缓存 | √ | × | × |
| 错误检查 | √ | × | × |
| 错误处理 | √ | × | √ |
| 请求拦截器 | √ | × | √ |
| 前缀 | √ | × | × |
| 后缀 | √ | × | × |
| 中间件 | √ | × | × |
| 取消请求 | √ | × | √ |
我们通常在src 目录下会生成一个services文件夹,来对接口进行统一管理。
public // 公共文件 可以放一些第三方字体 样式库等
mock // mock文件
src
|-- components // 公共组件目录 当业务需要拆分组件的时候,可以在对应的业务文件夹下单独创建一个components文件夹
|-- layouts // 项目结构文件
|-- locales // 规划文件
|-- models // 公共model存放位置
|-- public.js // 公共model文件 可以多个
|-- services // 公共api存放
数据层的管理用到了dva, dva是基于Redux和Redux-Saga的数据流方案,一个轻量级的应用框架。
- Page 负责与用户直接打交道:渲染页面、接受用户的操作输入,侧重于展示型交互性逻辑。
- Model 负责处理业务逻辑,为 Page 做数据、状态的读写、变换、暂存等。
- Service 负责与 HTTP 接口对接,进行纯粹的数据读写。
先创建Model
import { ServiceData } from '@/services'; // 是引入service层定义的请求接口方法名
export default{
namespace:'tableModel',
state:{
tabaleData:[]
},
reducers:{
//同步方法
saveTable(state,{payload}) {
return {...satate.tabaleData,...plyload}
}
},
effects:{
// 异步方法
* fetchTable(payload,{call,put}) {
const { data } = yield call(ServiceData, payload) // ServiceData是引入service层定义的请求接口方法的名字,payload是请求传递的参数,data是后台返回的接口
yield put({
type: "saveTable",// 这就是reducer中fetchTable方法, put就是用来触发上面reducer的方法,payload里就是传过去的参数。 同时它也能触发effects中其他方法。
payload: {
payload: data, // 把后台返回的数据赋值给了payload
},
}
}
}
- namespace 命名空间 唯一的名字
- state 初始数据
- reducers 同步的方法
- effects 异步的方法
使用effects定义异步方法,使用call方法调用Service层定义接口的方法,然后在使用put调用同步方法触发reducers, 类似Vuex的mutation方法,是数据唯一的提交方式,然后reducers把数据返回给state。
Index页面
import React from 'react';
import { connect } from 'dva'
const Index = ({tabaleData}) => {
return <List data={tabaleData} />;
}
const mapStateToProps = ({ tableModel }) => {
return {
tableData: tableModel.tabaleData,
};
};
const mapDispatchToProps = dispatch => ({
fetchTableList: (payload) => {
disdpatch({
type: 'tableModel/fetchTable',
payload,
});
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Index);
使用connect 让页面和Model进行连接,通过mapDispatchToProps用来调用Model层的方法,通过disdpatch派发一个对象进行触发,再使用mapStateToProps的props对应Model的namespace,返回一个对象,里面是获取Model的数据,组件接收props获取到数据在组件里使用。
dva 这样设计页面里只用接收porps数据展示,model层专注逻辑处理,这样虽然写起来会相对麻烦一些,但是代码写起来更加清晰明,后期可维护性也更高了,爱了爱了。