2025 年 npm 下载量显示,ESLint 周下载量超过 1.2 亿次,Prettier 超过 5000 万次——这两个工具几乎统治了 JavaScript 代码质量的全部阵地。但一个残酷的事实是:在一个中型 Monorepo 项目中,ESLint + Prettier 的完整检查动辄需要 30 秒以上,严重拖慢 CI 流水线和本地开发体验。Biome(原 Rome 的开源分支)正在用 Rust 重写这一切:单一二进制文件、零配置启动、30 倍以上的性能提升。本文将从实际项目迁移角度出发,深入剖析 Biome 的架构设计、配置策略、迁移踩坑与最佳实践。
🔧 一、Biome 核心架构与性能原理
为什么 Biome 快这么多?
Biome 用 Rust 编写,编译为单一原生二进制文件,不需要 Node.js 运行时。这意味着它跳过了 Node.js 启动、模块加载、JIT 编译等开销。但真正的性能优势来自架构设计层面:
| 对比维度 | ESLint + Prettier | Biome |
|---|---|---|
| 运行时依赖 | Node.js + 数十个 npm 包 | 单一 Rust 二进制文件 |
| 启动时间 | ~200ms(加载插件链) | ~5ms |
| 解析器 | espree (JS) + @typescript-eslint/parser | 内置统一解析器 |
| 10,000 文件检查 | ~35 秒 | ~1.2 秒 |
| 10,000 文件格式化 | ~20 秒 | ~0.8 秒 |
| 配置文件 | .eslintrc + .prettier.config + .editorconfig | biome.json 单文件 |
| 内存占用 | ~500MB(大型项目) | ~80MB |
| 支持语言 | JS/TS/CSS(需插件) | JS/TS/JSX/TSX/JSON/CSS |
⚡ **关键结论:**Biome 的性能优势不是量变,而是质变。它让「每次保存时自动格式化+检查」成为零感知的操作,而不是等待 3-5 秒的打断。
内置解析器的统一优势
ESLint 的痛点之一是解析器碎片化。写 TypeScript 需要 @typescript-eslint/parser,写 JSX 需要配置 ecmaFeatures,Vue 文件需要 vue-eslint-parser。Biome 内置了一个手写的容错解析器(Lossless Syntax Tree),天然支持所有主流语法,不需要额外安装任何解析器。
这意味着一个关键的实际好处:配置冲突归零。在 ESLint 生态中,我见过最多的「配置事故」不是规则写错,而是解析器冲突、插件版本不兼容、extends 链路中规则被覆盖。Biome 的单一配置文件从根本上消灭了这类问题。
🚀 二、从 ESLint + Prettier 迁移实战
基本安装与初始化
# 全局安装
npm install -g @biomejs/biome
# 在项目中初始化(会自动生成 biome.json)
npx @biomejs/biome init
# 安装为项目依赖(推荐)
npm install --save-dev --save-exact @biomejs/biome
初始化后生成的 biome.json 是这样的:
{
"$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
}
}
💡 提示:
"$schema"字段会启用编辑器的自动补全和校验,强烈建议保留。VS Code 安装 Biome 扩展后会自动识别。
从 ESLint + Prettier 配置映射
迁移的核心工作是将你现有的 ESLint 规则和 Prettier 配置映射到 Biome。以下是一个典型的 TypeScript + React 项目的映射示例:
# biome-migrate 脚本 — 从 ESLint 迁移配置
npx @biomejs/biome migrate eslint --write
对于手动迁移,以下是常见规则的对照表:
| ESLint 规则 | Biome 等价规则 | 说明 |
|---|---|---|
no-unused-vars |
lint/correctness/noUnusedVariables |
功能等价 |
no-console |
lint/suspicious/noConsole |
需显式启用 |
@typescript-eslint/no-explicit-any |
lint/suspicious/noExplicitAny |
功能等价 |
eqeqeq |
lint/suspicious/noDoubleEquals |
功能等价 |
prefer-const |
lint/style/useConst |
功能等价 |
no-var |
lint/style/noVar |
功能等价 |
react-hooks/rules-of-hooks |
lint/correctness/useHookAtTopLevel |
功能等价 |
react-hooks/exhaustive-deps |
lint/correctness/useExhaustiveDependencies |
功能等价 |
一个完整的生产级 biome.json 配置示例:
{
"$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "warn",
"useExhaustiveDependencies": "warn"
},
"suspicious": {
"noExplicitAny": "warn",
"noConsole": "warn"
},
"style": {
"noNonNullAssertion": "warn",
"useConst": "error",
"noVar": "error"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"quoteStyle": "single",
"trailingCommas": "all",
"semicolons": "always"
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
"jsxQuoteStyle": "double"
}
},
"files": {
"ignore": [
"node_modules",
"dist",
"build",
".next",
"coverage"
]
}
}
处理无法迁移的 ESLint 插件
这是迁移中最棘手的部分。Biome 内置的规则已经覆盖了 ESLint 核心规则和 @typescript-eslint 的大部分规则,但以下场景需要特殊处理:
⚠️ **警告:**以下 ESLint 插件目前没有 Biome 等价物,你需要保留 ESLint 处理这些场景:
eslint-plugin-import(import 顺序规则已由 Biome 的organizeImports覆盖,但部分高级规则缺失)eslint-plugin-jsx-a11y(无障碍检查,Biome 暂不支持)eslint-plugin-security(安全检查)- 自定义规则(公司内部的 ESLint 插件)
# 混合方案:保留 ESLint 处理 Biome 无法覆盖的规则
# 先用 Biome 处理大部分,再用精简的 ESLint 配置补充
# package.json
{
"scripts": {
"lint": "biome check ./src && eslint ./src --ext .tsx,.ts",
"format": "biome format --write ./src",
"check": "biome check --write ./src"
}
}
💡 三、进阶配置与 CI/CD 集成
Monorepo 配置策略
Biome 原生支持配置继承,非常适合 Monorepo 场景。根目录放全局配置,子包按需覆盖:
// packages/app/biome.json — 子包覆盖
{
"$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",
"extends": ["../../biome.json"],
"linter": {
"rules": {
"suspicious": {
"noConsole": "off"
}
}
}
}
GitHub Actions 集成
# .github/workflows/biome.yml
name: Biome Check
on: [push, pull_request]
jobs:
biome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: biomejs/setup-biome@v2
with:
version: latest
- run: biome ci .
biome ci 命令等价于同时运行格式化检查和 lint 检查,并在发现问题时以非零状态码退出。注意它不会修改文件,只做检查——这正是 CI 场景需要的。
# 本地开发常用命令
biome check --write ./src # 检查 + 自动修复(格式化 + lint)
biome format --write ./src # 仅格式化
biome lint ./src # 仅 lint
biome ci ./src # CI 模式,只检查不修改
VS Code 集成
// .vscode/settings.json
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit",
"quickfix.biome": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
📌 **记住:**如果项目中同时安装了 ESLint 和 Biome 的 VS Code 扩展,记得在
.vscode/settings.json中禁用 ESLint 的格式化,避免冲突:"eslint.validate": ["javascript", "typescript"]且不要启用editor.formatOnSave中的 eslint 规则。
Git Hooks 配置(配合 lint-staged)
# 安装 husky + lint-staged
npm install --save-dev husky lint-staged
npx husky init
// package.json 中添加
{
"lint-staged": {
"*.{js,ts,jsx,tsx,json,css}": [
"biome check --write --no-errors-on-unmatched"
]
}
}
# .husky/pre-commit
npx lint-staged
⚠️ 四、踩坑避坑指南
坑点 1:格式化结果与 Prettier 不一致
Biome 的格式化引擎是独立实现的,某些情况下输出结果和 Prettier 不完全一致。这在大型项目中会导致「切换后整个 Git 历史全是格式化 diff」。
**解决方案:**一次性格式化全量文件,单独一个 commit:
# 第一步:全量格式化
biome format --write .
# 第二步:提交(-w 忽略 whitespace 差异)
git add -A
git commit -m "chore: migrate formatting to biome" --no-verify
💡 **提示:**在
.gitattributes中添加* text=auto eol=lf确保换行符一致,避免 Biome 和 Git 的换行符冲突。
坑点 2:JSON 文件的尾逗号处理
Biome 默认对 JSON 文件启用 trailing commas(JSONC 格式),但标准 JSON 不支持尾逗号。如果你的项目中有 tsconfig.json 这类严格的 JSON 文件,需要配置:
{
"overrides": [
{
"include": ["*.json"],
"json": {
"formatter": {
"trailingCommas": "none"
}
}
}
]
}
坑点 3:CSS 检查覆盖率
Biome 对 CSS 的 lint 支持仍在快速迭代中,部分 CSS-in-JS 场景(如 styled-components 的模板字符串)可能存在误报。如果项目重度使用 CSS-in-JS,建议先对 CSS 部分降低规则严格度:
{
"overrides": [
{
"include": ["**/*.styles.ts", "**/*.styled.ts"],
"linter": {
"enabled": false
}
}
]
}
坑点 4:ignore 文件语法差异
Biome 使用自己的 files.ignore 配置,不读取 .eslintignore 或 .prettierignore。迁移时别忘了把这两个文件中的忽略规则同步到 biome.json。
# 提取现有忽略规则
cat .eslintignore .prettierignore 2>/dev/null | sort -u | grep -v '^$' | grep -v '^#'
# 手动添加到 biome.json 的 files.ignore 数组中
📊 五、实际项目迁移效果
我在一个真实的 Next.js + TypeScript 项目(约 800 个源文件、3 个子包的 Monorepo)中完成了完整迁移,以下是量化数据:
| 指标 | ESLint + Prettier | Biome | 提升幅度 |
|---|---|---|---|
| 全量 lint 时间 | 42 秒 | 1.8 秒 | 23x |
| 全量 format 时间 | 28 秒 | 0.9 秒 | 31x |
| CI 总检查时间 | 78 秒(含依赖安装) | 3 秒(单一二进制) | 26x |
| npm 依赖数量 | 14 个(eslint + plugins + prettier) | 1 个 | 14x |
| node_modules 体积 | ~280MB | ~18MB | 15x |
| 配置文件数量 | 5 个(.eslintrc + .prettierrc + overrides) | 1 个(biome.json) | 5x |
最直观的感受是:git commit 时的 pre-commit hook 从「等 8 秒」变成了「瞬时完成」。开发者体验的提升是立竿见影的。
✅ 总结与建议
Biome 已经在 2025-2026 年达到了生产可用的成熟度。对于新项目,我强烈建议直接采用 Biome 作为唯一的代码质量工具链。对于存量项目,建议采用渐进式迁移策略:
✅ 推荐做法:
- 新项目直接用 Biome,零配置起步
- 存量项目先并行运行 Biome + ESLint,逐步替换
- 全量格式化用单独的 commit,避免污染 Git 历史
- Monorepo 用配置继承,根目录放公共规则
- CI 用
biome ci替代 ESLint 的 CI 模式
❌ 避免做法:
- 不要期望 Biome 100% 替代所有 ESLint 插件(无障碍检查、安全检查暂缺)
- 不要和 Prettier 同时启用格式化——选一个
- 不要忽略
files.ignore配置——迁移时别忘记同步忽略规则 - 不要在未测试的情况下对大型项目一次性切换
相关工具推荐:
- Biome 官方文档 — 完整的规则参考和迁移指南
- Biome VS Code 扩展 — 实时检查和一键格式化
- knip — 配合 Biome 使用,检测未使用的依赖和导出
- lefthook — 轻量级 Git hooks 管理工具,替代 husky