持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
上一篇:React项目优化(1)-使用CommonModel处理通用业务数据
概述
在项目开发中,不论是初始化加载数据,亦或是点击按钮触发事件,在调用接口时,都会存在等待页面响应的情况。如此一来会产生两个问题:
第一:加载数据时,页面出现白屏现象。视觉效果不好。
第二:点击按钮时,没有响应,客户会多次点击。客户体验感不好。
优化前处理方式
在项目中loading效果使用了tinper-bee中的Loading组件。
import { Loading } from 'tinper-bee';
//调用
<Loading
fullScreen
showBackDrop={true}
loadingType="line"
show={this.state.showLoading}
/>
处理方式比较简单,在每次请求前数据前设置showLoading 为true,展示loading。
在请求完成后设置 showLoading 为false,隐藏loading 即可。
// 示例代码
loadData = async () => {
try {
const { searchId } = this.state;
await this.setState({ showLoading: true });
if (searchId) {
// 调用 getList 请求数据
const { result } = processData(await api.getDetail({ id: searchId }));
}
} catch (err) {
console.log(err);
} finally {
this.setState({ showLoading: false });
}
};
不足之处
- 繁琐工作量大 -- 每个请求都需要加手动控制显示/隐藏操作。
- 代码风格不一致 -- 有的开发将数据存在
store中,有些则将数据存放在redux中。有的使用的整个页面的Loading。有的只是按钮上显示loading。甚至,有人使用了Loading,有人直接直接控制按钮是否可点击。 - 新手或编程不规范的开发者,容易丢失,导致测试经常提类似的问题,维护成本大。
将Loading进行统一管理,势在必行。
解决思路
- 在
axios中,使用request拦截器和response拦截器,分别处理添加/删除loading的逻辑。 - 增加一个通用的
redux的Store在内部维护Loading显示和隐藏的逻辑。 - 在
Header组件中引入Loading组件。根据loadingVisible属性,控制是否显示 Loading。
解决方案
1. Axios拦截器统一处理loading状态
- 引入
actions用于将loadingVisible维护到commonModel中。 - 通过
requestCount控制 合适隐藏loading。
import axios from "axios";
import mirror, { Router, Route, actions, connect } from "mirrorx";
let x_xsrf_token = '', random_num = Math.random();
//用于处理连续多个 axios 请求时,loading 快速显示/快速关闭的问题;
let requestCount = 0;
// 请求之前拦截
axios.interceptors.request.use((reqCfg) => {
let commonModel = actions.commonModel;
requestCount++;
if (commonModel) {
commonModel.updateState({
loadingVisible: true,
})
};
return reqCfg
});
// // 响应之前拦截
axios.interceptors.response.use((resCfg) => {
let commonModel = actions.commonModel;
requestCount--;
if (commonModel && requestCount == 1) {
commonModel.updateState({
loadingVisible: false,
})
}
return resCfg;
});
export default (url, options) => {
// axios 配置
}
2. 通过commonModel维护通用数据
// ucf-common\src\utils\commonModel.js
export default {
name: "commonModel",
initialState: {
option: {}, //字典
dicOption: {}, //自定义档案
userInfo: {}, // 登录人信息
parameterInfo: {} // 参数值管理配置的数据;
},
};
3. CommonModel数据 的注入
// ucf-apps\fls-demo\src\routes\index.js;
// 组件引入
import List from "./list";
import Edit from "./edit";
// 数据模型引入
import model from "./model";
import commonModel from "utils/commonModel";
mirror.model(model);
mirror.model(commonModel);
// 数据和 组件UI 关联、绑定
const ListContainer = connect((state) => {
return { ...state.mainModel, ...state.commonModel, ...state.routing }
})(List);
const EditContainer = connect((state) => {
return { ...state.mainModel, ...state.commonModel, ...state.routing }
})(Edit);
// Index 组件, 注意调用方法来自于 commonModel
class Index extends React.Component {
componentDidMount() {
}
render() {
return (
<div className="route-content">
<Route exact path="/" component={ListContainer} />
</div>
);
}
}
export default Index;
4. 在Header组件 引入loading组件
由于在详情的根节点都会有Header组件。所以直接在Header组件内使用Loading,那么业务逻辑开发时,不需要显性处理Loading逻辑。
注意在使用Header的时候,需要传递 loadingVisible 属性。该属性的值来源于当前节点的props(model)。
// 路径: ucf-common\src\components\Header\index.js
const { title, back, children, loadingVisible } = this.props;
<Col xs={12}>
<Loading
loadingType="line"
showBackDrop={true}
show= {loadingVisible}
fullScreen={true} />
</Col>
两点心得体会
- 既然统一封装了Loading, 那么整个开发过程,所有参与者都应该遵守规范,统一使用封装后的Axios调用接口。避免自行引入axios,那样封装不起作用。
- 作用一个独立的单页面应用,在初次加载中,加载信息较多,所以会持续出现一段时间的白屏现象。我的解决方式如下:
在 React应用加载的宿主页面(index.html)加载如下一段html即可。
在“app”挂载之前,加载这个Css的
Loading动画。
<body>
<div id="app">
<div class="u-loading-backdrop">
<div class="u-loading u-loading-line">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
</body>