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

360 阅读5分钟

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

使用Mirrorx(Redux)管理数据

如图,这是一个标准的UCF-WEB的项目结构。

  • ucf-apps中,每个文件夹比如('fls-order','fls-contract'),被称为一个小应用(独立应用),这是一个单页面应用,包括列表及详情等信息。
  • ucf-common是一个公共内容文件夹,其内部包括项目通用的组件,共用的样式及常用的JS工具类。 微信截图_20220604164116.png

首先,从数据的通信谈起,一般而言,对于普通的React组件,类组件内,我们将数据存放在state中。父子组件使用props和回调函数的方式实现。其他比较复杂的组件关系。我们使用Redux来处理数据通信。

UCF-WEB工程中使用了mirrorxMirrorX是一个js库,是基于Redux封装的一种状态机。
所以,针对于每一个小应用,其内部都有一个独立的model.js文件。用于存放该应用内跨组件的数据。

model.js

import { actions } from "mirrorx";
export default {
// 确定 Store 中的数据模型作用域
    name: 'mainModel',
    // 设置当前 Model 所需的初始化 state
    initialState: {
        nodeKey: "fls-contract",
        funCode: "FLB0000",
    },
    reducers: {
        // 纯函数,相当于 Redux 中的 Reducer,只负责对数据的更新。
        updateState(state, data) {
            //更新state
            return {
                ...state,
                ...deepClone(data),
            };
        },
    }
}

关键的 index.js

在该文件内部通过mirrorxmirrorx方法,将storerouter和组件关联起来。

import mirror, { Router, Route, actions, connect } from 'mirrorx';
// 数据模型引入
import model from "./model.js";
mirror.model(model);
import listApp from './list';

let ListContainer = connect((state) => {
    return { ...state.mainModel, ...state.routing };
})(listApp);

通用业务数据

这里所说的通用业务数据包括:字典、自定义档案、按钮权限数据、登录详细信息以及自定义参数值。

  1. 字典:在项目的表单中下拉选择(Select)的选项数据,是通过接口查询,以数组的形式返回到页面中。我们称之为字典。
  2. 定义档案:类似于字典,但是这些数据是使用该系统的用户,在特定功能节点,根据业务需求,提前录入系统的数据。我们称之为自定义档案。
  3. 钮权限数据:这是控制页面按钮是否显示的配置数据。
  4. 录信息:由于cookie中无法获取用户的完整信息,我们会通过特定接口查询此信息。
  5. 数值数据:类似于自定义档案的一种数据。
  6. 他通用数据: 省市县数据,行业门类(大类,中类,小类)等。

以上这些数据,都有如下几个特点:

  • 每个小应用都会使用到。
  • 通过接口查询,只会读取,不会修改。
  • 查询参数固定,查询的结果也是固定的。

存在问题

目前项目的处理方式,在每个小应用中独立处理,通过接口查询,将数据存放在Reduxstore中或者存放在根组件的State中。 根据不同场景各自使用。 这样存在以下问题:

  • 代码冗余,繁琐。
  • 浪费开发时间,效率低。
  • 格式及代码风格不统一。
  • 后期维护成本高,不易维护。

处理方案

基于以上问题,我们进行了如下优化:

优化思路:将这些共用的数据提取出来,通过通用的CommoModel统一管理,具体应用开发者只需引入并使用,不管数据查询。

Tip:要明确的,这里说的CommonModel是一个跨越小应用的通用组件。和上文说的节点内维护共用数据的MainModel是不同的组件。

创建 CommonModel

ucf-common中创建commonModel.js文件

export default {
  name: "commonModel",
  initialState: {
    option: {}, // 字典
    dicOption: {}, // 自定义档案
    userInfo: {}, // 登录人信息
    parameterInfo: {}, // 参数值管理配置的数据
    authData: [], // 按钮权限数据
    divisionArr: [], // 行政区域数据
  },
  reducers: {
    updateState(state, data) {
      //更新state
      return {
        ...state,
        ...deepClone(data),
      };
    },
  },
  effects: {
     // 加载字典数据
     async loadDicItem(param, getState) {
         // todo ...
     },
     // 加载登录人信息 
     async getLoginInfo(params, getState) {
     },
     // 加载按钮权限数据
     async getBtnAuthInfo(param) {
     },
     // 加载其他通用数据
  }
}

引入并使用

在小应用的入口文件中引入并调用commonModel。 这个这些数据只需要加载是初始化一次,之后不用再调用相关接口。

// 引入mirrorx
import mirror, { Router, Route, actions, connect } from "mirrorx";

// 引入通用Model;
import commonModel from "utils/commonModel";
mirror.model(commonModel);

// 引入当前节点的Model;
import model from "./order/model.js";
mirror.model(model); 

// 数据和组件UI关联、绑定
let ListContainer = connect((state) => {
  return { ...state.mainModel, ...state.commonModel, ...state.routing };
})(listApp);

// 初始化调用commonModel中的方法; 
export class OrderRouter extends Component {
  componentDidMount() {
    let userId = getCookie("userId") || "";
    let paramsCode = ["ABC0000"]; // 查询参数值;
    actions.commonModel.loadDicItem({ codeList: dicItem }); // 字典;
    actions.commonModel.loadTenantDicItem({ codeList: tenantDicItem }); // 自定义档案;
    actions.commonModel.getLoginInfo({ id: userId });
    actions.commonModel.getParameterValue({ codeList: paramsCode });
    actions.commonModel.getDivisionData(); // 查询行政区划地址
    // 按钮权限数据;
    actions.commonModel.getBtnAuthInfo({
      funcCode: "ABC0000",
      r: uuid(),
    });
  }
} 

这个操作之后,我们的通用数据会直接注入到小应用中,节点开发人员只负责相应的业务场景,不在考虑通用数据的查询与维护。
当然,这个commonModel不止负责这些,还有其他用途,我们会另有文章介绍。


另外,有人会说,那这个commonModel中每个方法的调用还需要开发者自行写,并不是完全不用管的。 我想说:看官,您别急,这些还会继续优化,在后面的文章中,我们将学习如何自动生成这些文件。