半年一百个页面,重构系统也重构了我对前端工作的理解

为什么想去重构

在现在的部门已经呆了两年多,系统后台使用的是 vben2 的框架搭建的,旧框架版本滞后导致bug频发,重复代码堆积成"屎山",交互设计割裂影响用户体验,多环境维护成本翻倍......这些问题像藤蔓一样缠绕着日常开发,既消耗着大量时间精力,也让技术价值难以充分释放。

所以我一直盘算着如何去完成整个系统框架的升级,中间我一直关注 vben5 的开源进度,在 vben5 没有完成表单和表格的全部功能实现之前,对于系统的重构就始终搁置。但是中间有两个新项目我用了 vben3 框架搭建,使用下来的感受与 vben2 并没有太大的区别,没有达到我的预期,所以就继续等待 vben5 的进度。

之前组内前端是三个人,后来按业务划分我这条线就是两个前端在维护,上半年的时候与我共事的这位同事产假,于是所有的迭代压力自然而然的压到了我的身上,当时我的第一想法就是,这或许是全量重构系统的契机,原因有几点:

  • 一个人迭代可以准确了解每一个需求的进行情况,在新系统迁移过程中,不容易遗漏迭代中的需求功能。
  • 自己可以把握日常迭代的节奏,每周都能适当留出重构的时间。
  • 最后一点也是最重要的一点,就是 vben5 开源进度已经可以满足生产系统的需求。

如何理解重构

说到重构,有一本书可能大多数程序员都听过,叫《重构:改善既有代码的设计》,书中指出重构的定义为:在不改变软件可观测行为的前提下,调整代码结构,提高软件的可理解性,降低变更成本

书中提到我们要明确一点,重构不是一种道德行为,而是一种经济适用行为,如果它不能让我们更快更好的开发,那么它是毫无意义。具体点说就是代码的写法应该使别人理解它所需要的时间最小化,进而变更代码需要的时间也会最小化。

其次也是书中没有提到但我认为很重要的一点就是:脱离业务设计重构或者优化,最多只能是技术自证,始终是片面的。

作为前端开发者我们用着同样的计算机语言,却身处完全不同的业务场景下,重构代码的同时思考如何提升用户的使用体验,才是我们作为前端研发的价值体现。

总结重构落地的意义可以归结为以下几点:

  • 更快速的定位问题,节省调试时间。
  • 最小化变更风险,提高代码质量,减少修复事故的时间。
  • 提升用户体验。
  • 得到程序员同行的认可,更好的发展机会。

有了理论支持与明确的目标,我们就可以开始着手重构的设计构思。

开源框架本地化

从之前一直维护框架代码的经验来说,这些都是可以交给开源团队去维护,项目开发者只需要定期拉取最新版本的框架代码,更多的精力应当更专注于业务逻辑与功能实现,所以我在开源本地化的时候将 vben5github 地址一直作为基准的远程地址,去定期同步。

其核心维护流程图如下:

受益于 vben5 的架构,框架代码与业务代码做到了完全分离,什么改动是框架的,什么改动是业务的,定义就会十分明确。在日常迭代中遇到需要调整框架的,就去框架项目改,改完同步到所有的项目中去,这样既保证了框架版本的最新,同时迭代了本地化的框架改动,也不会影响到业务代码。

前端部署的良解

之前部门前端项目中的部署方式是通过本地打包上传到静态服务器(我这叫狮子座),然后在生产服务器启一个 node 服务来访问 index.html 文件。具体流程如下展示:

总结来说就是哪哪都会出问题,所以这次重构后我采用 服务器打包 + nginx服务 的方式,其实这套方式在很久之前我的另一篇文章【微前端qiankun+docker+nginx配合gitlab-ci/cd的自动化部署的实现】中实现过,所以我也轻车熟路地在公司的运维平台实现了自动化部署,其流程如下:

这套部署流程对于日常开发来说是非常省事且多环境的可拓展性也很强。

多环境的解决方案

目前公司项目的环境分为三个大环境,每个大环境有三个小环境,解释起来也很简单,就是公司每在一个地区落地,就要根据政策把服务器部署在本地,所以大环境就是地区服,每个地区服又要分测试环境、预发环境和生产环境,这样算下来一个项目就会有九个环境,目前的运作方式就是三个大环境三个 git ,每个 git 内再用三个环境分支分别用作部署,这样的弊端就是每次完成一个需求都要对另外两个 git 进行代码同步,而且时不时还会有不同环境不同需求的情况。久而久之,每个需求的工作成本基本上都要翻倍,因为要花时间去处理大环境同步及差异。

vben5 在处理环境使用的 dotenv ,在项目中都是通过 import.meta.env.xxx 访问环境变量,所以涉及到不同环境的差异代码写法如下:

vue 复制代码
<script lang="ts" setup>

const pageTitle = import.meta.env.VITE_PAGE_TITLE;
const pageDesc = import.meta.env.VITE_PAGE_DESC;
const isProduction = import.meta.env.VITE_GLOB_APP_BUILD_TYPE !== 'qa';

</script>

<template>
  <AuthPageLayout
    :is-production="isProduction"
    :page-description="pageDesc"
    :page-title="pageTitle"
  />
</template>

其中 VITE_PAGE_TITLEVITE_PAGE_DESC 在不同环境 env 文件中的定义如下:

env 复制代码
#.env.production.yc
VITE_GLOB_APP_BUILD_TYPE=product
# 环境常量
VITE_PAGE_TITLE=医疗后台-银川
VITE_PAGE_DESC=欢迎登录银川医疗后台!

#.env.production.nj
VITE_GLOB_APP_BUILD_TYPE=product
# 环境常量
VITE_PAGE_TITLE=医疗后台-南京
VITE_PAGE_DESC=欢迎登录南京医疗后台!

对应的运行与打包指令如下:

json 复制代码
{
  ...
  "scripts": {
    "build:yc": "pnpm vite build --mode production.yc",
    "build:nj": "pnpm vite build --mode production.nj",
    "dev:yc": "pnpm vite --mode development.yc",
    "dev:nj": "pnpm vite --mode development.nj",
  },
  ...
}

结合之前的部署方案,只需要在Dockerfile 中指定对应的 build 指令即可完成对应环境的打包部署。

组件化和模块化

简单来说,模块化"拆逻辑",将复杂功能拆分为独立的逻辑单元,解决代码复用和依赖管理问题;组件化"拆 UI",将页面拆分为独立的 UI 单元,解决界面复用和维护问题。 但在实践过程中,会对这两个概念有新的认知与补充。

比如说我现在有一个复杂的表单页面,涉及到两个表单以及多个动态表格,以模块化的思维大量的逻辑函数将会被定义,又因为表单表格具有独特性,不存在复用的场景,所以按理说写到一个vue文件中即可。结果就导致了单个文件的代码量会达到一个让人抓狂的地步,而且后续的维护,大家也会有一种惯性就是,之前都这么写的我再往后面加就行。

另一个场景,有一个业务弹窗组件复用性很高,但是每次都需要对组件引入、注册、状态管理、销毁等等操作,重复的代码好像也会有很多,如果是别人看到这种调用方式,可能还会放弃复用你的组件。

这肯定不是解决问题的方式,所以这时候我们需要对组件化与模块化有更多的理解。

  • 对于有明确UI单元的功能但没有复用场景,也应拆分成独立组件,复用只是自然结果,而非唯一目的。
  • 将具有复用性与需要独立管理生命周期的组件进一步封装,采用 useHook 方式进行调用。

首先,没有复用场景的组件在引入页面组装功能的时候,类似语意化的html标签,对比拆分单个方法的模块化最小单元,这可以说是更高一级的模块化方式,将一堆具有逻辑关联的模块与UI单元内聚到一个文件中,在定位需求或者问题的时候能够快速找到最核心的一块代码,从而提升效率。

vue 复制代码
<template>
  <div>
    <Card title="基本信息" class="mb-4">
      <BaseInfoForm>
        <template #comboGoods="slotProps">
          <ComboGoodsTable ref="comboGoodsTableRefs" v-bind="slotProps" />
        </template>
      </BaseInfoForm>
    </Card>
    <CouponProductTable ref="couponProductTableRef" @change="generateComboOptions" />
    <VipProductTable ref="vipProductTableRef" @change="generateComboOptions" />
  </div>
</template>

其次,对具有生命周期的组件进行hook式封装是非常实用的一种方式,比如我现在有一个日志弹窗,需要在系统各处实用,那么我们先对弹窗组件进行封装,如下:

vue 复制代码
<script lang="ts" setup>
...

const [Modal, modalApi] = useVbenModal({
	...
  onOpened() {
    logInfo.value = modalApi.getData<Record<string, any>>();
    gridApi.query();
  },
});

const gridOptions: VxeTableGridOptions<LogRowType> = {
  ...
};

const [Grid, gridApi] = useVbenVxeGrid({
  gridOptions,
});
  
defineExpose({ modalApi });
</script>
<template>
  <Modal class="w-[780px]" title="日志">
    <Grid />
  </Modal>
</template>

如果直接对上面的组件进行调用是这样的:

vue 复制代码
<script lang="ts" setup>
  import Log from '#/components/LogModal/Log.vue';
  
  const log = ref<InstanceType<typeof Log>>();
  
  const openLog = ()=> {
    log.value.modalApi.setData({ logType: 1, objectId: 1 }).open();
  }
</script>
<template>
  <div>
    <Log ref="log" />
    <Button @click="openLog">日志</Button>
  </div>	
</template>

其实 vben5 对于弹窗组件已经有了很好的封装,已经能用很简单的方式进行弹窗的调用,虽然组件内部已经管理好弹窗的显隐,但是我们依旧要引入组件并且写到页面结构中去,这时候我们可以写一个 hook 去再次封装 Log 组件。

typescript 复制代码
import type { ExtendedModalApi } from '@vben/common-ui';
import type { VxeGridPropTypes } from '@vben/plugins/vxe-table';
import { h, render } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import Log from './Log.vue';

export function useLog(): {
  LogModalApi: {
    open: (data: {
      /** 日志类型 */
      logType: number;
      /** 日志对象参数 */
      objectId?: number | string;
    }) => void;
  };
} {
  let container: HTMLElement | null = null;
  let currentModalApi: ExtendedModalApi | null = null;

  const createModal = () => {
    if (container?.parentNode) {
      render(null, container);
      container.remove();
    }

    container = document.createElement('div');
    container.setAttribute('id', 'log-modal');
    document.body.append(container);

    const [Component, ModalApi] = useVbenModal({
      connectedComponent: Log,
      onClosed: () => {
        if (container) {
          render(null, container);
          container.remove();
        }
        container = null;
        currentModalApi = null;
      },
    });

    const Node = h(Component, {});
    render(Node, container);

    return ModalApi;
  };

  return {
    LogModalApi: {
      open: (data) => {
        currentModalApi = createModal();
        currentModalApi.setData(data).open();
      },
    },
  };
}

Log 组件二次封装后,调用方式就变成了如下写法:

vue 复制代码
<script lang="ts" setup>
import { useLog } from '#/components';

const { LogModalApi } = useLog();

const log = () => {
  LogModalApi.open({ logType: 1, objectId: 1 });
};
</script>

<template>
  <div @click="log">日志</div>	
</template>

进一步精简了业务页面上的代码量,当然如果有额外需求,只需要对 useLog 进行参数拓展以及 Log 组件业务拓展。

总结

  • 组件化的核心是"边界隔离",而非 "代码复用"
  • "组件 + Hook"的模式,实现了"能力复用" 而非"代码复制"

数据处理与优化思路

我相信大多数前端开发者和我有同样的感受,就是后端返回的数据结构很多时候不是我们想要的,需要我们重新处理,或者说我们在做数据处理的时候更多的是考虑功能的实现,至于可维护性要么交给注释,要么没有。于是"屎山代码"的雏形慢慢形成。

于是可以归纳的前端项目难以维护的几个点是:

  • 数据结构不匹配是常态 :后端往往从业务逻辑、数据库设计、性能角度返回数据(比如嵌套过深、字段命名不统一、冗余字段多、数组结构不符合前端渲染需求),前端为了实现功能,只能在代码里加大量转换逻辑,比如循环遍历重组数组、解构嵌套对象、字段映射(把user_name转成userName)、过滤冗余数据等。
  • 初期只关注功能实现,忽略可维护性:开发时为了赶进度,这些转换逻辑往往写得零散(比如在组件里直接写、在请求回调里硬编码),既没有抽成通用工具函数,也没有统一的处理规范,顶多加几句注释(甚至不加)。
  • 技术债务逐步堆积 :当需求迭代(比如后端字段调整、前端渲染逻辑变化),这些零散的转换逻辑会被反复修改、叠加,最终变成改一处崩多处的"屎山代码"------比如 A 组件改了字段映射,B 组件忘了同步,导致页面报错;或者新人接手时,根本看不懂一堆map/filter/reduce到底在处理什么。

其实很多前端性能上的问题都是在这一过程中损失的,即使我们做好了首屏加载、打包文件分割、样式优化等等,有时我们还会觉得"卡卡的",那些对对象滥用的操作,都在无形中增加对内存的损耗,可以参考我之前的文章【从V8引擎的角度去看看JS对象模型的实现与优化】

所以需要明确的是,前端的核心价值是"交互和渲染" ,而非 "数据结构的二次重构",过度的前端数据处理属于 "无效的技术成本"。前端不是"后端数据的被动加工者",而是和后端协同定义数据的"参与者",过度的前端数据加工会埋下可维护性的坑,而高效的前后端协作能从根源减少无意义的前端逻辑堆积。

其实话说到这前端中间层 的必要性已经呼之欲出了,把数据结构适配的成本从前端代码层转移到前后端协商层 ,或许才是这个问题最合理的解决方式,既不用后端改变固有设计与逻辑,也不用前端二次数据开发。当然开发中间层是有很大的成本在里面的,是选用低成本的多沟通还是前端花时间自己在中间层处理想要的数据,这就要根据每个人当前工作环境而定了。

当然做中间层我们也要明确中间层的职责:

  1. 避免过度设计:中间层只做数据适配,不做业务逻辑(比如订单计算、权限判断仍由后端核心服务负责),否则会变成新的 "屎山"。
  2. 保持轻量:中间层的接口要一对一适配前端页面 / 组件,避免设计成通用接口(比如一个接口适配多个页面,后期改造成本高)。

总而言之,前端要跳出"被动加工数据"的思维,用协作降低无意义的开发成本

交互复杂度

在AI盛行的当下,作为一名前端开发,如果还是只注重功能实现,那么网上铺天盖地的AI取代前端或许真的不遥远了。

前面提到前端的核心价值是"交互和渲染",所以我理解前端应当从「功能实现导向」转向「用户体验 + 工程效率双导向」的设计与开发思维。首先说两个我在日常开发或者做重构的基本思维:

  • 交互复杂度是决定用户体验的重要因素。
  • 表单设计应当以降低交互复杂度为主要考量,复杂逻辑简化交互,简单交互高度组件化。

说到这有人可能会觉得这应该是设计或者交互该考虑的事,但是实际情况是前端有时候必须考虑一些除技术外的东西,且不说很多公司是没有交互这一个专门的职位,大多数后台是没有交互设计的,就是拿组件去堆,设计很多时候也只是根据产品原型图去实现,能给到开发的图其实是很平面化的,开发处理不好好各个页面或者组件之间的衔接交互,是直接影响用户体验的。

之前看过一个统计,说每多一个弹窗用户留存率降低70%,看到这个数据我的第一感觉是比我预想的要多很多,这也更加确立了我在处理任何带有弹窗交互需求的时候都会考虑一下是否真的有必要。

我上面也单独把表单设计拎了出来,因为表单是前端最常见但也最易出体验问题的场景,其核心痛点就是「交互复杂度失控」:

  • 要么把复杂业务逻辑直接暴露给用户(比如多步骤、多校验、多关联字段的表单,让用户反复填、反复改)
  • 要么把简单交互过度工程化(比如一个单行输入框,拆成多层组件、加一堆冗余逻辑,导致开发效率低、维护成本高)

所以我的解决思路就是:

表单场景 核心问题 解法核心 最终目标
复杂逻辑的表单 交互复杂度高→用户易出错 / 放弃 简化交互(把复杂藏在底层) 降低用户认知 / 操作成本,提升完成率
简单交互的表单 开发效率低→重复造轮子 高度组件化(封装通用能力) 提升开发效率,保证交互一致性

表单的本质是「用户完成信息录入的工具」,而非 "展示技术能力的载体":

  • 用户对表单的核心诉求是快速、准确、无阻碍地完成提交,而非体验炫酷的交互。
  • 交互复杂度越高,用户的认知负荷和操作成本就越高,最终表现为表单完成率低、错误率高。
  • 反之,即使业务逻辑复杂(比如电商商品编辑、优惠券编辑),只要把交互拆解得足够简单(比如分步引导、字段联动自动填充、错误提示精准),用户也能顺畅完成。

这一点也印证了 UX 领域的经典原则:"把复杂留给自己,把简单留给用户" ------ 表单体验的好坏,本质是 "开发者能否把复杂的业务逻辑,转化为用户可感知的简单交互"。

针对以上问题,C端通常有业务、产品、设计多个部门在把关,相对来说前端的实现上的主观性会弱很多,而B端则没有这么多要求,也就通常是表单设计的重灾区。虽然B端不存在用户留存率,但是难用的操作逻辑确实增加了用户对研发的不满情绪。

有一次业务和我吐槽配了一些营销策略到系统中,本来逻辑不复杂,但是配置花了将近一下午。从那之后,我在做表单类的需求时,潜意识都会考虑这个表单除了完成功能之外到底好不好用,所以我后来在做系统重构时,将所有涉及到复杂表单的场景全部进行了彻彻底底的交互上的重构,原来需要两个、三个弹窗处理的配置,全部设计成最多一个弹窗完成。当然重新设计的过程中也要时刻与产品业务做好沟通与同步,询问他们的意见,了解用户的真实需求。

正确理解需求迭代

相信大家都玩过俄罗斯方块,我觉得每一个需求就是一个下落的方块,把它落在哪个地方,如何和已有的方块密切贴合,或者给未来落下的方块腾空间,决定了这个游戏我们能玩多久。

需求迭代不应是功能的简单堆砌,而是功能的不断整合。 要想维护好前端的工程化,我们必须从「被动接需求」变为「主动做治理」,需求迭代的本质不是 "加一行代码、加一个功能",而是通过整合(收敛冗余)、优化(提效降本)、重构(解耦升级) ,让代码体系随需求增长更有序而非更臃肿,所以需求迭代就成了持续重构系统边界、收敛重复逻辑、提升整体内聚性的过程。

为什么 "功能堆砌" 是前端的致命伤?

前端最容易陷入 "需求来了就加代码" 的堆砌式迭代,最终导致:

  • 逻辑冗余:相似功能写多套(比如 3 个页面都有 "状态筛选",却写了 3 套筛选逻辑,原因就是每次封装的组件业务性强,最后复用性变差)
  • 交互碎片化:同一类操作(比如 "弹窗关闭")在不同页面有不同交互,用户体验割裂(长期迭代下没有对之前过时的交互进行整合)
  • 维护成本指数级上升:改一个基础逻辑(比如 "手机号校验规则"),要改 10 个地方
  • 技术债务堆积:旧代码不敢动,新需求只能绕着写,最终形成 "屎山"

那么,前端如何落地 "整合 + 优化 + 重构" 的迭代思维?

整合

整合是我在做系统重构的过程中最早规划的,核心是把分散、重复的功能逻辑收敛到统一的模块 / 组件中。要想把整合做好,最重要的是熟知业务,如果没有完整便捷的文档,想要完整了解业务最好是通过阅读代码逻辑抽丝剥茧,总结流程图、逻辑图。

1、识别 "相似功能",收敛到通用层

正如前文中提到的 Log 组件,在重构的视角下,能从全局角度去总结所有场景下的通用性,不重复写弹窗逻辑,而是抽象 通用日志弹窗组件 + useLog Hook,把 "日志类型、数据结构" 等作为参数,让所有日志弹窗复用同一套逻辑;核心思维就是从 "为单个需求写功能" 转向 "为一类需求抽象能力"

2、整合 "碎片化交互",统一交互规范

在公司之前的系统中,涉及到表格内编辑时,有时需要点击编辑切换整行可编辑状态,有的需要弹出一个表单再编辑,有的则可以直接在行内进行编辑,碎片化的交互随处可见。整合之后,所有的交互变为单元格点击编辑的方式,不再自定义交互,避免体验碎片化。

3、整合 "数据请求 / 处理逻辑",收敛到中间层 / Hook

因为使用 vben5 框架,对于请求数据处理的封装已经全部收敛到对应的模块中去,不需要再去重复造轮子。实践来说比如全局对用户信息的调用频繁,那我们可以把用户信息的请求、数据转换、缓存逻辑封装到 useUser Hook 中,所有页面通过 Hook 获取数据,迭代时只需维护 Hook 内的逻辑。

优化

"优化" 是在整合基础上,对现有逻辑做 "减法" 和 "提效",核心是「消除冗余代码、提升性能 / 开发效率」,而非 "加新功能"。

1、代码层面:剔除冗余逻辑,简化实现

冗余逻辑都是从哪来的?根据我的开发经验来说,所有类似"这段逻辑先留着,以后可能会用到"、"老的逻辑/组件/页面别删,加一个新的就好"这些话术,其本质就是开发过程中的偷懒,因为不想多花点时间去了解前后业务逻辑,将所有的不确定留在项目代码中,技术债务成本随之而来。

即使未来真的可能需要用到,一是可以从提交记录中恢复,二是即使能用到大概率也需要调整,在理解老代码的逻辑过程中,或许都已经自己实现了。

2、性能层面:明确优化方案,准确突破性能瓶颈

我面试过很多人,问到前端优化方案,无非都是首屏加载、打包文件分割、样式优化这些,再问一下具体场景的分析与实现,意为你通过什么样的分析判断页面需要进行什么样的优化时,大多数人又磕磕巴巴说不出个所以然来。

其实我们完全可以通过浏览器内置的分析工具,去分析页面加载、页面渲染、页面节点数、页面事件数、内存占用等等各种指标,去发现问题所在,那么我们再去优化时就会明确自己的优化预期是什么样的,而不是随便从网上找一堆优化大杂烩堆到项目中去。

3、开发层面:优化交互方式,降低接入成本

多考虑弹窗交互是否可以优化,通过合理的交互设计,避免使用多弹窗交互。还有我之前提到的 useLog 组件的实现,进一步的封装不仅极致简化组件的调用代码,也让 "整合后的能力" 更易用,避免 "整合后反而增加开发成本"。

当然除了组件复用的优化还有接口复用,最常见的就是字典类型的接口,用于各种需要调用后端接口的下拉框,这时候我们可以合理利用 vue-query 这种缓存库,对接口返回信息进行缓存,降低接口的重复调用率,不仅节省了服务器资源,也让前端的体验更优。

重构

"重构" 是在整合 / 优化的基础上,对 "不合理的架构 / 边界" 做调整,核心是「让代码体系适配长期需求,而非仅满足当下」------ 重构不是 "推翻重写",而是 "小步迭代式升级"。重构需要我们在某一段时间花费大量时间去思考架构/模块的设计,不仅是为了找出当下最优解,也要为未来可能的升级或改变留下空间。

1、边界重构:拆分 "职责混乱" 的模块

其实我在做系统重构的时候经常会推翻之前某一类代码的写法,比如我写一个表格内的单元格编辑省缺值组件,一开始可能只是在某个编辑场景中使用,所以写法也没太多的封装。

vue 复制代码
<script lang="ts" setup>
import { isEmpty } from '@vben/utils';

const props = defineProps<{
  placeholder?: string;
  prefix?: string;
  value: any;
}>();

const {
  value,
  placeholder = '点击编辑',
  prefix = '',
} = props;
</script>
<template>
  <div>
    <span class="cursor-pointer" v-if="!isEmpty(value)">
      {{ prefix }}{{ value }}
    </span>
    <span class="cursor-pointer text-gray-400" v-else>
      {{ placeholder }}
    </span>
  </div>
</template>

再后来的重构过程中,我发现处理表格内编辑省缺值的情况很多,而且每次遇到写在表格的 slot 中很麻烦,于是将代码中所有适用的情况都找了一下,几次完善后,组件最终如下:

vue 复制代码
<script lang="ts" setup>
import { isEmpty, isNumber } from '@vben/utils';

const props = defineProps<{
  booleanMap?: {
    falseText: string;
    falseValue?: any;
    trueText: string;
    trueValue?: any;
  };
  multiple?: boolean;
  options?: Array<{ label: string; value: any }>;
  placeholder?: string;
  prefix?: string;
  type?: 'boolean' | 'number' | 'select' | 'string';
  value: any;
}>();

const {
  type = 'string',
  value,
  booleanMap = {
    trueText: '是',
    falseText: '否',
    falseValue: false,
    trueValue: true,
  },
  options = [],
  multiple = false,
  placeholder = '点击编辑',
  prefix = '',
} = props;

const formatSelected = (val: any) => {
  if (multiple) {
    return options
      .filter((item) => val.includes(item.value))
      .map((item) => item.label)
      .join(',');
  }
  return options.find((item) => item.value === val)?.label ?? val;
};
</script>
<template>
  <div>
    <template v-if="!isEmpty(value)">
      <span class="cursor-pointer" v-if="type === 'string'">
        {{ prefix }}{{ value }}
      </span>
      <span
        class="cursor-pointer"
        v-else-if="type === 'number' && isNumber(Number(value))"
      >
        {{ prefix }}{{ value }}
      </span>
      <span class="cursor-pointer" v-else-if="type === 'boolean'">
        {{
          value === booleanMap.trueValue
            ? booleanMap.trueText
            : booleanMap.falseText
        }}
      </span>
      <span class="cursor-pointer" v-else-if="type === 'select'">
        {{ formatSelected(value) }}
      </span>
    </template>
    <span class="cursor-pointer text-gray-400" v-else>
      {{ placeholder }}
    </span>
  </div>
</template>

边界重构的核心实现还是组件化,在开发中不断回头发现可以整合优化的地方,就像玩俄罗斯方块一样把它们放在一排,然后就可以安心消除(安心调用)了。

2、架构重构:升级通用层,分离框架与业务

我最喜欢 vben5 的地方在于它很好的实现了 monorepo 架构,场景化拆分解决了中后台项目重复封装通用能力的痛点 。所有非业务的代码都可以放在 packages 中,项目代码对通用能力的调用就如同引用一个三方库,这让我在重构业务代码和学习框架代码之间切换明确,也让我在重构的过程中有了更多的思考,如:如何将一个业务通用能力抽象为一个框架通用能力。

3、小步重构:避免 "大重构风险"

做好这点原则是每次迭代只重构 "当前需求涉及的不合理逻辑",而非一次性重构整个模块。其实我在做系统重构就是一整个系统的大重构,在重构落地的过程中会发现各种风险无法规避,不管是测试压力,还是需求同步,都是越到后面越难推进,这也是大重构最大的副作用。所以想要规避,就要明确重构的边界,确保测试可以完全冒烟。重构不是一个人的事,是协调产品、测试这些上下游对口部门的统一规划。

注释

当你感觉需要写注释时,请先尝试重构,试着让所有注释都变得多余。

如果你不知道该做什么,这才是注释的良好运用时机。除了用来记述将来的打算之外,注释还可以用来标记你并无十足把握的区域。你可以在注释里写下自己"为什么做某某事"。这类信息可以帮助将来的修改者或者健忘的自己。

研发价值

我不知道在AI盛行的当下,大家有没有考虑过现在的研发价值到底是怎么体现的。根据 Linux 基金会发布的《2025 年全球科技人才状况报告》,AI 正在重塑工作,而非消灭工作,这种 "重构革命" 深刻改变着科技行业的人才需求格局,推动研发人员的价值认知转型。

很多人认为做业务开发显得没那么有挑战性,但其实正好相反。最难解决的bug是无法重现的bug,最难处理的问题域是不确定性的问题域。业务往往是最复杂的,面向不确定性设计才是最复杂的设计。软件工程学科最难的事情是抽象,因为它没有标准、没有方法、甚至没有对错。如何在软件固有的复杂性上找到一条既不过度也不缺失的路,是软件工程师的终身课题,或许永远也无法达到,或许我们已经在路上了。

综上所述,我理解的研发价值分为三个阶段:

  1. 技术知识的掌握、技术选型的判断力,以及用技术解决实际问题的能力
  2. 对业务逻辑的深度理解、日常业务问题的快速解决,以及通过系统整合 / 优化提升业务效率与用户体验的能力
  3. 准确理解各方(产品、业务、测试)意图,在沟通中降低协作摩擦、提供情绪价值,给予最优实现方案

一阶段

作为技术研发,技术知识的掌握一定是我们立身的根本,一方面是技术的深度,另一方面是技术的广度。技术深度决定了我们的技术能力和解决问题能力,技术广度决定了我们如何理解不同技术栈之间的协作关系,能够根据项目需求进行合理的技术选型。

企业研发的技术能力,不只是 "会用技术",更重要的是 "选对技术"。比如:能用 Vue3 写页面是运用能力,但能判断这个后台项目用 Vue3+Element Plus,还是React+Ant Design,能解决 "大数据量表格卡顿" 这类实际问题,才是企业需要的技术价值。

基于AI发展迅速的大环境下,基础技术的价值正被快速剥离,技术人的价值重心正在悄然发生改变。生成式 AI 在代码领域的应用已进入成熟期,基础代码编写效率提升数倍,一个能够用好 AI 的研发人必然有了额外的价值。或许在不远的未来,传统的 "代码编写" 能力将不再是核心竞争力,研发人员能够定义 AI 工作边界,掌握 AI 工具的使用和优化,学会与 AI 协作完成技术工作或许才是最终目标。

二阶段

有了技术基础,我们开始逐渐融入不同的业务中去,正如业内专家所言,"理解业务的深刻程度决定了发展上限"。这种理解不是简单的需求翻译,而是能够穿透业务表象,理解功能背后的用户痛点、商业逻辑和战略目标。

所以处理日常问题只是业务能力的基础,通过深度理解业务,将业务问题转化为技术问题,用技术手段实现业务目标。 同时也要求我们具备技术前瞻性,能够预判技术趋势对业务的影响,提出具有创新性的技术解决方案。所以一阶段的基础越扎实,二阶段的业务整合就会更游刃有余。

三阶段

这一阶段本质上是有了前两阶段沉淀后的协作共赢,其核心价值在于通过高效协作放大个人和团队价值 。最优实现方案在企业中往往不是"技术最优",而是 "业务价值、研发成本、时间周期"三者平衡的全局最优方案 ;同时,"情绪价值" 的核心是对齐预期、降低协作摩擦,比如:产品提了一个复杂需求,研发不是直接说 "做不了",而是说 "这个需求可以拆成两步,第一步先实现核心功能,成本低、见效快,第二步再优化体验"------ 这既提供了情绪价值,又给出了可行方案。

在沟通协调能力方面,研发人员需要具备跨职能团队协作能力,能够与产品、运营、测试、设计等团队有效沟通,协调各方利益,推动项目顺利进行。这种能力不仅体现在技术沟通上,更体现在能够用非技术语言向业务人员解释技术方案,用业务语言向技术人员传达需求。要想做好这些,前两阶段的积累必不可少。

最后

因为在重构过程中不断向vben5 github提交issuespull request,我也有幸成为了开源共建者。

在着手这次重构时,我不会想到自己最后会有这么多思考与感悟,可能有人会觉得系统能用就行,为什么要去折腾,我想用《人月神话》中我最喜欢的一句话作答。

这个行业需要我们掌握更先进的开发要素和工具,经论证的管理方法和最佳应用,良好判断的自由发挥,以及保持谦卑。

相关引用

系统困境与软件复杂度,为什么我们的系统会如此复杂

《重构:改善既有代码的设计》

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
yunteng5217 小时前
通用架构(同城双活)(单点接入)
架构·同城双活·单点接入