简单清理掉项目中没用的180+文件

遇到的痛点

这篇文章或许有另一个不太优雅的名字--"屎山治理"。

在繁重的业务开发当中,我们会面临一些问题。伴随着项目的不断发展,项目出现代码冗余,存在大片没用代码的情况。

举个栗子,重构优化后,某位同学没有删除掉冗余代码,项目残留着废弃的没用文件,导致跨文件引用混乱。还有,业务变更所导致逻辑代码的废弃,项目中重复的定义代码,这些情况在一个长期的项目发展的阶段里面会造成逻辑混乱,重复定义,二义性等等。

其实,程序员都是写代码的,但是很少人敢删代码,久而久之,也就没人敢动废弃代码了。

虽然在项目构建工具的加持下,tree-shaking能够控制项目的包产物体积,但是从开发体验(DX)的角度出发,这往往都是一些心智负担。结合我自己的一些优化经验,简单分享一下:

优化手段

手段一:eslint的unused检查

首先我们应该考虑的是,通过 eslint 的规则有效的去规避一些项目当中已有的没用的变量和方法,这样保证单文件代码的可用性,我们可以很容易的发现哪个import或者variable没有被使用。import的冗余控制也能够有效控制打包的范围,减少包体积。

eslint最常用的就是官方的no-unused-vars这一条规则。

当然还有一些,第三方的unused-exports规则,例如eslint-plugin-canonical的no-unused-exports或者eslint-plugin-unused-imports,这种大家可以适度采用,毕竟eslint是把"双刃剑"。

手段二:静态代码工具扫描

通过一些静态分析工具可以有效地分析代码语法,根据语法树信息来判断内容是有用还是没用。

ts-unused-exports是一个很成熟的分析工具,它可以通过 ts-compiler 对 typescript代码语法进行分析,(tsconfig可以配置allowjs,分析javascript语法),通过TS语法树有效地找到语法中没用的 export。

该工具能够把所有的没用的 export 找到。这时候我们会很自然地想到一个问题,能否找到完全没有使用的废弃文件。这里分两种情况,情况一,该文件所有的 export 都已经被废弃了,这种情况出现在代码重构的情况,另外一种情况是部分的export没有被使用,那这种需要case by case的判断,到底这个代码有没有存在意义?

暂时这个工具只能找到所有的 export 函数,并没有文件粒度,并不能满足我们的"诉求"。我们希望能把完全没用的文件直接删除掉,所以我提了一个issue。

我查看了源码,parse过后,会通过getExportMap获取每个文件,且它的所有exports内容。我写了一个PR,在和作者沟通交流下,尽量以最小的 api 改动情况来处理。利用一个参数findCompletelyUnusedFiles来控制是否找出完全没有被使用的文件,参考PR#254

改动涉及最核心内容,如下。将该文件的真实所有 export 和 unused export 作对比,以此判断它是完全没用的文件。

javascript 复制代码
const realExportNames = Object.keys(exports);

if (
  extraOptions?.findCompletelyUnusedFiles &&
  isEqual(realExportNames, unusedExports)
) {
  unusedFiles.push(path);
}
});

当我们得到了这个结果后,我们可以通过自己编写的脚本"大胆"的删除文件了。

在删除脚本内,我们要想清楚几个事情:

  1. 有范围的扫描(避免错删,所有改动在可控的范围内)
  2. 后缀名白名单(多市场的代码可能会存在"多态",例如,id代表印尼,index.id.ts它不应该被清除掉)
javascript 复制代码
const result = analyzeTsConfig('./tsconfig.json', ['--findCompletelyUnusedFiles']);

const outputPattern = ['/pages/partner/', '/pages/store/', '/pages/staff/', '/services/'];
const excludePattern = ['.id.', '.my.', '.ph.', '.sg.', '.vn.', '.th.', '.br.'];

function filterOutput(name: string) {
  for (let index = 0; index < outputPattern.length; index++) {
    if (name.includes(outputPattern[index]) ) {
      return true;
    }
  }
  return false;
}

function filterExclude(name: string) {
  for (let index = 0; index < excludePattern.length; index++) {
    if (name.includes(excludePattern[index]) ) {
      return false;
    }
  }
  return true;
}

const { unusedFiles, ...rest } = result;

Object.keys(rest)
.filter(r => filterOutput(r))
.filter(r => filterExclude(r))
.map((key) => {
  const exportNames = rest[key].map(r=> r.exportName).join(',')
  console.log(chalk.green(key) + ' ' + exportNames);
}) 

if(result.unusedFiles) {
  console.log('no used files: ');
  result.unusedFiles
  .filter(r => filterOutput(r))
  .filter(r => filterExclude(r))
  .forEach((r) => {
    fs.unlinkSync(r);
  })
}

手段三:人工调整已有代码的合理性

在删除完代码后,项目中 ts-unused-export 还会扫描出一些部分 export 废弃的文件,我们只能按照自身的情况做出调整。每个团队的代码分层情况有所不同。这些文件可能不需要改动,也可能是需要调整该纯函数位置。我们应该把它们放在合理的位置。

总结

首先"清除废弃代码"是一个低频操作。可能我们一年或者几年,清理一次即可,保证代码的"清爽"。所以放在 webpack 等构建工具执行反而不太合适,脚本偶尔扫描,把一些废弃代码清干净,你的DX(developing experience)又回来了。

当然你忍受能力很强也可以"不做"。这篇文章适合具有轻度"代码强迫症"的同学食用。

PS:加餐,也可以参考knip,功能更强大噢。

相关推荐
IT_陈寒2 小时前
Python开发者必知的5大性能陷阱:90%的人都踩过的坑!
前端·人工智能·后端
codingWhat3 小时前
介绍一个手势识别库——AlloyFinger
前端·javascript·vue.js
Lee川3 小时前
深度拆解:基于面向对象思维的“就地编辑”组件全模块解析
javascript·架构
勤劳打代码3 小时前
Flutter 架构日记 — 状态管理
flutter·架构·前端框架
代码老中医3 小时前
2026年CSS彻底疯了:这6个新特性让我删掉了三分之一JS代码
前端
进击的尘埃3 小时前
Web Worker 与 OffscreenCanvas:把主线程从重活里解放出来
javascript
不会敲代码13 小时前
Zustand:轻量级状态管理,从入门到实践
前端·typescript
踩着两条虫3 小时前
VTJ.PRO 双向代码转换原理揭秘
前端·vue.js·人工智能
扉川川3 小时前
OpenClaw 架构解析:一个生产级 AI Agent 是如何设计的
前端·人工智能
远山枫谷3 小时前
一文理清页面/组件通信与 Store 全局状态管理
前端·微信小程序