React项目优化(2)-使用Axios+CommonModel统一处理Loading

912 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

上一篇:React项目优化(1)-使用CommonModel处理通用业务数据

概述

在项目开发中,不论是初始化加载数据,亦或是点击按钮触发事件,在调用接口时,都会存在等待页面响应的情况。如此一来会产生两个问题:
第一:加载数据时,页面出现白屏现象。视觉效果不好。
第二:点击按钮时,没有响应,客户会多次点击。客户体验感不好。

优化前处理方式

在项目中loading效果使用了tinper-bee中的Loading组件

import { Loading } from 'tinper-bee';
//调用
<Loading 
    fullScreen 
    showBackDrop={true} 
    loadingType="line" 
    show={this.state.showLoading} 
 />

处理方式比较简单,在每次请求前数据前设置showLoadingtrue,展示loading。 在请求完成后设置 showLoadingfalse,隐藏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进行统一管理,势在必行。

解决思路

  1. axios中,使用 request拦截器和response拦截器,分别处理添加/删除loading的逻辑。
  2. 增加一个通用的reduxStore在内部维护Loading显示和隐藏的逻辑。
  3. Header组件中引入Loading组件。根据loadingVisible属性,控制是否显示 Loading。

解决方案

1. Axios拦截器统一处理loading状态

  1. 引入 actions用于将 loadingVisible 维护到 commonModel中。
  2. 通过 requestCount 控制 合适隐藏loading
import axios from "axios";
import mirror, { RouterRoute, 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({
            loadingVisibletrue,
        })
    };
    return reqCfg
});

// // 响应之前拦截
axios.interceptors.response.use((resCfg) => {
    let commonModel = actions.commonModel;
    requestCount--;
    if (commonModel && requestCount == 1) {
        commonModel.updateState({
            loadingVisiblefalse,
        })
    }
    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>

两点心得体会

  1. 既然统一封装了Loading, 那么整个开发过程,所有参与者都应该遵守规范,统一使用封装后的Axios调用接口。避免自行引入axios,那样封装不起作用。
  2. 作用一个独立的单页面应用,在初次加载中,加载信息较多,所以会持续出现一段时间的白屏现象。我的解决方式如下: 在 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>