Knip:JavaScript 与 TypeScript 项目终极死代码检测器
Source: Dev.to
Knip:终极的 JavaScript/TypeScript 项目死代码检测工具
在现代前端和后端项目中,代码库的体积会随着时间的推移而不断增长。未使用的函数、未引用的文件以及多余的依赖会导致:
- 构建时间变慢
- 产出体积增大
- 维护成本上升
虽然 ESLint、TSLint 等工具可以捕获一些未使用的变量,但它们并不能完整地检测整个项目层面的死代码。Knip 正是为了解决这个痛点而诞生的——它能够在项目级别发现未使用的文件、导出、依赖以及脚本。
目录
为什么需要死代码检测?
- 提升性能:删除未使用的模块可以让打包工具(如 Webpack、Vite、Rollup)更好地进行 tree‑shaking,从而生成更小的 bundle。
- 降低技术债务:随着团队成员的更迭,旧代码往往被遗忘。及时清理可以防止“代码腐烂”。
- 安全性:未使用的依赖仍然会被安装到
node_modules,可能带来潜在的安全漏洞。
提示:即使是
devDependencies中的未使用包,也会在 CI 环境中被安装,增加构建时间和攻击面。
Knip 的核心特性
| 特性 | 描述 |
|---|---|
| 文件级别检测 | 找出项目中未被任何入口文件引用的 .js/.ts 文件。 |
| 导出级别检测 | 标记未被其他模块使用的命名导出(export const foo = …)。 |
| 未使用的依赖 | 检测 package.json 中声明但未在代码中 import/require 的依赖。 |
| 未使用的脚本 | 识别 package.json 中未被 npm run 调用的自定义脚本。 |
| 可配置的忽略列表 | 通过 knip.json 或 package.json 中的 knip 字段自定义排除规则。 |
| 快速且增量 | 采用 TypeScript 编译器 API,支持增量分析,适合大型 monorepo。 |
| CI 集成 | 可在 GitHub Actions、GitLab CI 等环境中直接返回错误码,阻止死代码进入主分支。 |
快速上手
1. 安装
# 使用 npm
npm i -D knip
# 使用 Yarn
yarn add -D knip
# 使用 pnpm
pnpm add -D knip
2. 运行基本检测
npx knip
默认情况下,Knip 会读取项目根目录的 package.json、tsconfig.json(或 jsconfig.json)以及 knip.json(如果存在),并输出类似下面的报告:
✖ Unused files (3)
src/utils/oldHelper.ts
src/components/LegacyButton.tsx
src/services/unusedService.ts
✖ Unused exports (5)
src/constants/index.ts: export const DEPRECATED_FLAG
src/hooks/useOldHook.ts: export function useOldHook()
...
✖ Unused dependencies (2)
lodash
moment
3. 在 CI 中使用
在 GitHub Actions 中加入一步:
- name: Run Knip
run: npx knip --fail-on-unused
--fail-on-unused 参数会在检测到任何未使用的项目时返回非零退出码,从而让 CI 任务失败。
配置选项详解
Knip 支持两种配置方式:
- 独立的
knip.json - 在
package.json中的knip字段
下面给出一个完整的 knip.json 示例(已翻译注释):
{
// 项目根目录(相对路径或绝对路径)
"projectRoot": ".",
// 指定入口文件,Knip 将从这些文件开始递归分析
"entry": [
"src/index.ts",
"src/server.ts"
],
// 要忽略的文件或目录(支持 glob)
"ignore": [
"src/**/*.test.ts",
"scripts/**",
"dist/**"
],
// 对特定文件的自定义规则
"paths": {
"src/generated/**": {
// 对生成的代码不进行任何检测
"ignore": true
}
},
// 依赖检测的细粒度控制
"dependencies": {
// 将这些依赖视为“始终使用”,即使未在代码中出现也不报错
"alwaysUsed": ["react", "react-dom"]
},
// 导出检测的排除列表
"exports": {
// 对某些文件的导出不进行检测
"ignoreExportsFrom": ["src/types/**"]
},
// 脚本检测
"scripts": {
// 将这些脚本标记为“已使用”,即使未在 CI 中调用
"alwaysUsed": ["build", "dev"]
}
}
常用 CLI 参数
| 参数 | 作用 |
|---|---|
--entry <paths> | 手动指定入口文件(覆盖配置文件中的 entry)。 |
--ignore <globs> | 在命令行层面添加忽略规则。 |
--fail-on-unused | 检测到未使用的项目时返回非零退出码。 |
--json | 以 JSON 格式输出报告,便于后续自定义处理。 |
--watch | 监听文件变化,实时重新分析(适合本地开发)。 |
与其他工具的对比
| 工具 | 检测范围 | 增量分析 | 配置灵活性 | CI 集成 |
|---|---|---|---|---|
| ESLint (no-unused-vars) | 仅变量/函数级别 | ✅(依赖 ESLint 本身) | 中等 | ✅ |
| ts-prune | 未使用的导出 | ❌(全量扫描) | 低 | ✅ |
| depcheck | 未使用的依赖 | ❌(全量扫描) | 中等 | ✅ |
| Knip | 文件、导出、依赖、脚本 | ✅(增量) | 高 | ✅ |
结论:如果你需要一次性覆盖 所有层面的死代码(包括未引用的文件和脚本),Knip 是目前最完整的解决方案。
常见问题与最佳实践
Q1: 为什么某些文件仍然被标记为未使用?
- 动态导入:Knip 只能静态分析
import/require。如果你使用import()动态加载模块,需要在knip.json中手动将这些文件加入ignore。 - 插件/框架约定:例如 Next.js 会自动加载
pages目录下的文件。可以在配置中添加paths→ignore来排除这些约定目录。
Q2: 如何在 monorepo 中使用 Knip?
- 在根目录放置统一的
knip.json,使用projectRoot指向各子包。 - 为每个子包单独运行
knip,并通过--entry指定子包的入口文件。 - 使用
--json输出统一报告,再通过自定义脚本聚合。
Q3: 是否可以自动删除检测到的死代码?
Knip 本身只负责检测并报告。不建议直接在 CI 中自动删除文件,因为这可能导致误删。推荐的流程:
- 生成报告(JSON)。
- 在 PR 中附加报告链接或自动生成的评论。
- 开发者手动审查并删除。
如果你确实想实现自动化,可以结合 git rm 与自定义脚本,在本地或专用的 “cleanup” 分支上执行。
最佳实践清单
- 在 CI 中使用
--fail-on-unused,防止新死代码进入主分支。 - 在本地开发时开启
--watch,实时获取未使用的导出提示。 - 为动态导入的文件添加显式的
ignore,避免误报。 - 定期(如每月一次)运行全量分析,清理长期未使用的依赖。
结论
Knip 通过 文件、导出、依赖以及脚本 四个维度提供了全方位的死代码检测能力,配合增量分析和灵活的配置选项,能够轻松融入现代 JavaScript/TypeScript 项目的开发与 CI 流程。无论是单体项目还是大型 monorepo,Knip 都是提升代码质量、降低构建体积、减少安全风险的实用工具。
立即在你的项目中尝试 Knip,告别冗余代码,让代码库保持轻盈与健康!
问题:死代码无处不在
每个成熟的代码库都有一个不为人知的脏秘密:死代码。
有人写的“以防万一”的工具函数。
被废弃特性的组件。
为一次试验而安装的 npm 包,却从未真正使用。
ESLint 能捕获文件内部未使用的变量和导入,但以下情况怎么办:
- 从未在任何地方被导入的文件?
- 没有人使用的导出函数?
- 在
package.json中忘记删除的依赖? - 定义了却从未引用的类型和接口?
这就是 Knip 的用武之地。
什么是 Knip?
Knip(荷兰语意为 “cut”)是一个强大的静态分析工具,能够在你的 JavaScript/TypeScript 项目中查找 未使用的文件、依赖和导出。可以把它想象成代码库的 Marie Kondo —— 如果代码不带来喜悦(或未被使用),就得删掉。
什么是 Knip 检测
| 类别 | 描述 |
|---|---|
| 🗂️ 未使用的文件 | 未在任何地方导入的源文件 |
| 📦 未使用的依赖 | package.json 中未使用的包 |
| 📤 未使用的导出 | 已导出但从未被导入的函数、类、类型 |
| 🔧 未使用的开发依赖 | 不需要的开发包 |
| ❓ 未列出的依赖 | 代码中使用但在 package.json 中缺失的包 |
| 🔗 未解析的导入 | 指向不存在模块的导入 |

入门指南
安装
# npm
npm install -D knip
# yarn
yarn add -D knip
# pnpm
pnpm add -D knip
基本用法
npx knip
就这么简单!Knip 会分析你的项目并输出它发现的所有未使用的代码。
配置
对于大多数项目,Knip 开箱即用。对于复杂的设置(monorepo、自定义入口等),你需要一个配置文件。
在项目根目录创建 knip.json:
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/index.ts", "src/App.tsx"],
"project": ["src/**/*.{ts,tsx}"],
"ignore": [
"**/__tests__/**",
"**/__mocks__/**",
"**/node_modules/**"
],
"ignoreDependencies": [
"prettier",
"husky"
],
"ignoreExportsUsedInFile": true
}
关键配置选项
| 选项 | 描述 |
|---|---|
entry | Knip 开始追踪的入口文件 |
project | 需要分析的文件 |
ignore | 要跳过的文件/模式 |
ignoreDependencies | 要跳过的依赖(对仅作配置的包很有用) |
ignoreExportsUsedInFile | 不报告仅在同一文件中使用的导出 |
React Native 示例
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/App.tsx", "index.js"],
"project": ["src/**/*.{ts,tsx}"],
"ignore": [
"**/__tests__/**",
"**/__mocks__/**",
"android/**",
"ios/**"
],
"ignoreDependencies": [
"@react-native/metro-config",
"@react-native/typescript-config",
"@react-native/babel-preset",
"patch-package",
"husky",
"reactotron-react-native"
],
"ignoreExportsUsedInFile": true
}
真实世界的结果
在生产环境的 React Native 应用上运行 Knip,得到以下结果:
Unused files (73)
src/components/map/index.tsx
src/components/views/NotificationMenuButton.tsx
src/models/requests/auth/authRequests.ts
src/utils/helpers/contactHelper.ts
... and 69 more
Unused dependencies (1)
@react-native-firebase/perf
Unused devDependencies (4)
@babel/preset-env
@testing-library/jest-native
@types/react-native-get-random-values
eslint-plugin-react-you-might-not-need-an-effect
Unlisted dependencies (2)
credit-card-type src/features/paymentMethods/addNewCard/index.tsx
Unused exports (74)
translations src/config/localization/languages.ts
defaultAddressForm src/features/addressBook/addressTypes.ts
... and more
73 个未使用的文件! 这可能是成千上万行的死代码,导致:
- 增加包体积
- 让新开发者感到困惑
- 增加维护负担
- 出现在搜索结果中,浪费时间
将其集成到您的工作流中
NPM 脚本
将以下内容添加到您的 package.json:
{
"scripts": {
"knip:check": "knip",
"knip:fix": "knip --fix"
}
}
CI/CD 集成
# .github/workflows/code-quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
knip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx knip
预提交钩子(使用 Husky)
# .husky/pre-commit
npx knip --no-exit-code
使用 --fix 自动修复
npx knip --fix
⚠️ 警告: 这会修改您的文件!请始终审查更改并准备好版本控制。
--fix 能做到的:
- ✅ 删除未使用的导出
- ✅ 从
package.json中删除未使用的依赖 - ❌ 不能删除未使用的文件(风险太大)
提示与最佳实践
- 使用
--include开始聚焦 – 如果结果让你不知所措,先将分析限制在特定文件夹或类别。 - 定期运行 Knip(例如在 CI 中),防止死代码累积。
- 将 Knip 与代码审查清单配合使用:“没有新的未使用的导入/文件”。
- 修复后,提交更改并告知团队清理已完成。
通过采用 Knip,你将保持代码库精简,提升开发者体验,并发布更小、更快的包。祝清理愉快!
Knip 使用技巧
1. 包含特定检查
# Only check for unused files
npx knip --include files
# Only check dependencies
npx knip --include dependencies
# Check multiple categories
npx knip --include files,exports
2. 选择输出报告器
# JSON output (great for tooling)
npx knip --reporter json
# Markdown (for documentation)
npx knip --reporter markdown
3. 忽略已知的误报
有些代码是通过动态方式或约定使用的,Knip 无法检测到:
{
"ignore": [
"src/generated/**",
"**/*.stories.tsx"
],
"ignoreDependencies": [
"tsconfig-paths"
]
}
4. 处理 Barrel 导出
如果你使用 barrel 文件(index.ts 重新导出所有内容),Knip 可能会报告误报。启用:
{
"ignoreExportsUsedInFile": true
}
5. 框架特定插件
Knip 内置支持多种框架:
{
"next": {
"entry": ["pages/**/*.tsx", "app/**/*.tsx"]
},
"jest": {
"config": ["jest.config.js"]
},
"storybook": {
"entry": [".storybook/main.ts"]
}
}
Knip 与其他工具对比
| 工具 | 未使用的文件 | 未使用的导出 | 未使用的依赖 | 自动修复 |
|---|---|---|---|---|
| Knip | ✅ | ✅ | ✅ | ✅ |
| ESLint | ❌ | ❌ | ❌ | 部分 |
| ts‑prune | ❌ | ✅ | ❌ | ❌ |
| depcheck | ❌ | ❌ | ✅ | ❌ |
| unimported | ✅ | ❌ | ✅ | ❌ |
Knip 是 一站式解决方案,可替代多个工具。
常见问题与解决方案
“我得到太多误报!”
- 验证代码是否是动态导入的。
- 在配置中将模式添加到
ignore。 - 对仅用于配置的包使用
ignoreDependencies。 - 启用
ignoreExportsUsedInFile。
“Knip 在我的大型 monorepo 中很慢”
{
"ignore": [
"**/dist/**",
"**/build/**",
"**/coverage/**"
]
}
“它没有找到我的入口点”
显式定义它们:
{
"entry": [
"src/index.ts",
"src/cli.ts",
"scripts/*.ts"
]
}
结论
死代码是会随时间累积的技术债务。每一个未使用的文件都是:
- 发送给用户的字节
- 开发者的认知负担
- 潜在的安全漏洞
- 浪费的 CI/CD 分钟
Knip 为您提供对代码库中实际使用情况的可视化。运行一次——您可能会对发现的内容感到震惊。定期运行,保持代码库精简且易于维护。
# Try it now
npx knip
资源
Knip 是否帮助你清理了代码库?在评论中分享你的结果吧!我很想知道你找到了多少死代码。 🔪
标签: javascript, typescript, webdev, productivity