2023.3.8,我在组内进行工作汇报。内容记录如下:
本次重构的特点
- 改动大
- 影响后续开发
所以有必要进行工作汇报,让组内同事了解代码的改动与现状。
为什么要重构代码?
正在开发的数据报告模块包含大量 widget 功能,并且它和仪表板模块中已有的 widget 功能大部分是相同的。
当然,数据报告模块会新增一些内容:新增“维度标签”组件、“度量标签”组件等。也会有一些局部的修改。
那么,需要复用widget 代码。但是现在的代码不能直接复用,所以需要重构。
重构要解决的两个问题
- widget代码的位置
- widget代码的内容
widget代码的位置
问题:
原先 widget 代码是在 Dashboard 模块下,需要把它挪出来。
解决方案:widget 独立成包
首先要明确:“widget 包对外只导出一个组件(类似 echarts 的调用方式),所有操作都在 widget 包内处理” 是无法实现的。
因为 WidgetView.vue 和 WidgetSettingsPanel.vue 是分开调用的。
所以 widget 包需要对外导出很多内容。使用的时候按需调用:
<template>
<Widget :data="data" />
<WidgetSourceDataDialog :data="data" :dialogVisible="dialogVisible" />
</template>
<script>
import { Widget } from "@bi/widget";
import { WidgetSourceDataDialog } from "@bi/widget";
import { updateRenderData } from "@bi/widget";
</script>
widget 独立成包。单独放在一个仓库中。
和主应用的代码分开存放,这样独立性更高。代码耦合性降低,有利于维护代码。
确定了方案,开始施行。但是不到两天就进行不下去了,遇到两个非常棘手的问题:
麻烦1:widget -> a -> b -> c,主应用中的很多内容也被迫独立成包
widget 包中引用了主应用的很多内容:
- commonUtils 工具方法
- globalConst 业务全局常量
- src/components/ui.vue 全局ui组件
- src/components/业务组件.vue 全局业务组件(不止 widge 用到,其他模块也会用到)
- dataset/api.js
这些内容不应该放在 widget 包中。但是放在主应用中的话,widget 又引用不到了。
可以把这些内容都当作参数,调用 widget包的组件和方法时传参进去。但这样很不合理。
所以只能把这些内容也独立成包,这样包和包之间可以互相调用了:
But,这是一个连锁反应:如果 widget 依赖a,a独立成包了。a又依赖了b,同理b也需要独立成包。
这样一步步找下去,主应用中的大部分代码都被提溜起来了,主应用就不剩多少东西了。。
会出现这种情况,是因为之前的代码之间互相引用比较随意,导致现在重构难以进行。以后的开发要吸取教训
麻烦2:少了一层 namespace
原先引用的时候,可以把文件名看作命名空间。比如下面两个方法,都叫 barHelper,但是能知道一个是拼样式的bar工具类,一个是拼数据的bar工具类。
import { barHelper } from "./styleUtils.js";
import { barHelper } from "./dataUtils.js";
widget独立成包后,再调用的时候有统一的入口。这时候看到 barHelper,就不确定是做什么的了。应该改成类似 barStyleHelper 的名字才合理:
import { barHelper } from "@bi/widget";
以上两个大麻烦,都是我在设计方案之初没有预料到的。尤其是第一个麻烦,工作量巨大,相当于要把整个项目中的代码重新梳理一遍。而且改动不是渐进性的,必须一次改完,不然项目跑不起来。
我当初选中这个方案,就是因为看中它分割的彻底性。现在不得不放弃这个方案,也是因为它分割的太彻底了,改动的工作量太大。
换一个方案
widget 代码还放在主应用中:只是从 Dashboard 文件夹下挪出来,直接放到 src 下
widget代码的内容
重构要解决的第二个问题:widget代码引用了外部环境(主要是仪表板)
- 外层组件 provide/props 提供进来的的一些参数
- widget 组件本身主动调用 store 中的内容
dashboardPalette;
dashboardTheme;
dashboardWidgetBgColor;
dashboardPermission
dashboardIsReadonly;
dashboardSize
pageType;
解决方案:appType
store中新增全局变量 appType,标志 widget 当前环境的类型:dashboard / dataWord。
在 widget 代码中统一使用 appXXX
appXXX = appType ==='dashboard' ? dashboardXXX : dataWordXXX;
在 DashboardContainer.vue 的 created 中 setApptype(“dashboard”)
在 DatawordContainer.vue 的 created 中 setApptype(“dataWord”)