使用 Knip 清理无用文件和依赖
每个 JavaScript 和 TypeScript 项目都会随着时间推移积累杂物:为快速实验安装的依赖、重构后遗留的工具文件、不再被任何地方导入的导出函数。单独来看,这些似乎无伤大雅。但累积起来,它们会拖慢构建速度、膨胀 node_modules、制造安全噪音,并让代码库难以维护。
传统的处理方式是手动审计:用 grep 搜索包名、查找文件引用、逐项核对 package.json。这种方法虽然可行,却难以规模化。这正是 Knip 的用武之地。
核心要点
- 未使用的依赖和死代码会增加构建时间、使打包产物膨胀,并带来安全和许可证方面的噪音。
- Knip 将整个代码仓库作为项目级 linter 进行分析,报告未使用的文件、导出、依赖以及未在清单中列出的导入。
--fix标志可自动应用清理操作,而--allow-remove-files则进一步支持删除未使用的文件。- 在 CI 中运行 Knip 可防止死代码累积,并能与 ESLint 及格式化工具形成良好互补。
为什么值得清理无用依赖和死代码
package.json 中未使用的包不仅仅是表面上的杂乱。它们会:
- 增加安装时间和
node_modules的磁盘占用 - 如果打包工具没有正确进行摇树优化(tree-shake),可能会被打入生产环境的产物中
- 在 Dependabot 等工具中触发误报的安全警告
- 可能附带在技术层面适用于你项目的限制性许可证
- 引入具有同样问题的传递依赖
死代码也有类似的代价。未使用的导出和未被引用的文件让人难以理解代码库的实际功能,并会拖慢那些仍需处理它们的 linter 和类型检查器。
Knip 的独特之处
像 depcheck 和 ts-prune 这样的工具曾解决过这一问题的一部分,但两者都在 2025 年被归档。Knip 应运而生,作为一款现代化的替代品,将依赖、导出和文件分析集成在一个持续维护的工具中。
可以把 Knip 看作一个项目级 linter。ESLint 检查单个文件,而 Knip 则分析整个仓库:入口点、导入图、导出以及 package.json 综合在一起。它会报告未使用的文件、未使用的导出、未使用的依赖,以及被导入但未在 package.json 中声明的包。
Knip 支持 npm、pnpm、Yarn 和 Bun,并需要 Node.js 20.19+ 或 Bun 运行环境。
五分钟快速上手
在项目中初始化 Knip:
npm init @knip/config
这会创建一个带有合理默认配置的 knip.json 配置文件。然后执行分析:
npx knip
Knip 会按问题类型分组输出报告:未使用的文件、未使用的导出、未使用的依赖以及未列出的依赖。在较老项目首次运行时,列表可能会很长——这是正常现象。
Discover how at OpenReplay.com.
安全地应用修复
当你审阅过报告并确认无误后,Knip 可以应用可审查的自动修复:
npx knip --fix
这会从未使用的导出中移除 export 关键字、清理 package.json,并处理 re-export 和 TypeScript 枚举成员。如果还想删除未使用的文件:
npx knip --fix --allow-remove-files
在提交前务必使用 Git 审查更改。 Knip 的设计本身比较保守,但自动修复确实会修改真实文件。在 stage 之前先执行一次 git diff 是良好的习惯。
你还可以针对特定类型的问题进行修复,从而让变更范围更小、更易审阅:
npx knip --fix-type dependencies
npx knip --fix-type exports,types
当 Knip 需要一些辅助时
Knip 并非一个完美的黑盒。某些场景需要手动配置:
- 动态导入(
import(someVariable))无法被静态解析 - 框架约定(如 Next.js 页面文件、Vite 虚拟模块)可能需要插件配置或忽略规则
- 生成的文件通常应排除在分析之外
- Monorepo 开箱即用效果不错,但跨工作区的引用有时需要显式配置
如果出现误报,建议在 knip.json 中添加 ignore 或 ignoreDependencies 条目,而不是禁用整块分析功能。
让清理成为日常习惯
Knip 最有效的使用方式不是一次性审计,而是定期运行。将其与 linter 和类型检查器一同加入 CI 流水线:
npx knip --reporter compact
让每个 Pull Request 都保持报告干净,可以从源头防止死代码的积累。将 Knip 与 ESLint(用于检查文件内未使用的变量)以及你选用的格式化工具搭配使用,便构建了一套稳健的自动化维护循环,让 JavaScript 和 TypeScript 项目保持精简,无需手动侦查工作。
结论
Knip 将查找未使用文件、导出和依赖这件繁琐的工作转化为一个可重复、自动化的步骤。把清理作为日常工作流的一部分,而非偶尔的大扫除,就能让项目保持精简、构建保持快速、依赖面保持紧凑。从一次 npx knip 开始,把结果纳入 CI,让工具替你完成过去靠人工排查的侦查工作。
常见问题
是的,Knip 默认只报告问题,除非传入 --fix 标志,否则不会做任何更改。可以先运行查看报告,再使用 --fix-type 逐类增量应用修复。在提交前务必审查 Git diff,使用 --allow-remove-files 时尤其要小心。
Knip 在单个持续维护的包中整合了这两款工具的职能。Depcheck 侧重于依赖、ts-prune 侧重于未使用的 TypeScript 导出,而 Knip 跨工作区综合分析文件、导出和依赖,对现代项目能给出更准确的结果。
误报通常源于动态导入、框架约定或生成的文件,这些情况下 Knip 无法静态解析。先检查是否存在适用于你框架的 Knip 插件,对剩余情况在 knip.json 中添加 ignore 或 ignoreDependencies 条目。避免直接禁用整块分析功能,以保持覆盖度。
可以,而且这是它价值最大的地方。在 CI 流水线中加入 npx knip --reporter compact,让每个 Pull Request 都检查新增的死代码。结合 ESLint 和格式化工具,这能构建一套自动化维护循环,防止未使用的文件和依赖在两次发布之间持续累积。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.