编程 Biome 深度拆解:当 Rust 决定「干掉 Prettier + ESLint」——一个 25K Star 的 Web 工具链如何用 35 倍性能重新定义代码质量守护

2026-08-04 00:14:48 +0800 CST views 12

Biome 深度拆解:当 Rust 决定「干掉 Prettier + ESLint」——一个 25K Star 的 Web 工具链如何用 35 倍性能重新定义代码质量守护

引言:前端工具链的「至暗时刻」

2026 年的前端开发,依然在承受着一个荒诞的现实:两个互不相干的工具,各自独立地扫描你的代码,各自维护各自的配置文件,各自消耗各自的 CI 时间,然后互相打架。

Prettier 负责格式化,ESLint 负责检查。你装两个包,维护两份配置(.prettierrc + .eslintrc),跑两次命令,等两份报告。如果 ESLint 的 --fix 和 Prettier 的格式化产生冲突,恭喜你,你刚刚发现了前端工具链的「薛定谔态」——代码在两种格式之间反复横跳。

更糟糕的是:ESLint 是用 JavaScript 写的。Prettier 也是用 JavaScript 写的。 在一个数万行文件的大型项目中,它们的扫描时间可以轻松突破 30 秒。你的 CI 管道因此变慢,你的开发者体验因此变差,你的耐心因此耗尽。

是时候有人来做点什么了。

Biome 就是那个「做点什么」的人。

它用 Rust 重写了整个工具链——格式化、Lint、导入排序、编辑器集成——全部在一个二进制文件中完成。它的格式化速度比 Prettier 快 35 倍,Lint 速度比 ESLint 快 15 倍。它已经拥有了 25,000+ GitHub Stars,被 Google、Microsoft、Vercel、Cloudflare、Discord 等顶级公司在生产环境中使用。

这篇文章将从架构到实战,深度拆解 Biome 的每一个核心设计决策,以及它如何从 Rome 的废墟中重生,最终成为 Web 工具链的事实标准。


第一章:从 Rome 到 Biome——一段「凤凰涅槃」的开源史

1.1 Rome 的雄心与陨落

2020 年,Facebook(现 Meta)的 Sebastian McKenzie——也是 Babel 的创造者——发起了 Rome 项目。目标宏大到令人窒息:用一个统一工具取代 Prettier、ESLint、Babel、webpack 的配置层,实现格式化、Lint、编译、打包的全栈一体化。

Rome 用 Rust 编写,性能惊人。在 2020-2021 年的 benchmark 中,它的格式化速度比 Prettier 快 25 倍。社区一片欢呼。

但问题很快显现:野心太大,资源不足。 Rome 试图同时解决太多问题——格式化、Lint、打包、编译——每个方向都需要大量的工程投入。2022 年底,Meta 的团队重心转移,Rome 项目的活跃度骤降。

2023 年 1 月,核心维护者 Emanuele Stoppa 和 Nicolas Hedger 做出了一个关键决定:分叉 Rome,专注于格式化和 Lint,放弃打包和编译。 项目更名为 Biome,意为「生态系统」——暗示它将成为 Web 开发生态的基础设施。

1.2 Biome 的差异化定位

分叉后的 Biome 做了一个看似「保守」实则精明的决策:不追求大而全,而是把格式化和 Lint 做到极致。

这个决策的逻辑很简单:

  1. 格式化和 Lint 是每个前端项目的刚需,而打包工具有 Vite、Rspack 等替代品
  2. 把两个工具合并为一个,消除配置冲突和重复扫描,本身就是巨大的价值
  3. Rust 的性能优势足以让它在速度上碾压所有 JavaScript 实现

到 2026 年,Biome 已经发布了 v2.5 版本,拥有 514 条 Lint 规则,97% 的 Prettier 兼容性,以及完整的 LSP(Language Server Protocol)支持。它不再是 Rome 的「阉割版」,而是一个成熟、独立、被广泛采用的 Web 工具链。


第二章:架构深度拆解——Rust 如何让一切快 35 倍

2.1 整体架构:单二进制,零依赖

Biome 的架构可以用一句话概括:一个 Rust 二进制文件,包含所有功能,零外部依赖。

biome (单个二进制文件)
├── 词法分析器 (Lexer)
├── 语法解析器 (Parser) → CST (具体语法树)
├── 格式化器 (Formatter) → 从 CST 直接输出格式化代码
├── Lint 引擎 (Linter) → 从 CST 运行规则
├── 导入排序器 (Import Organizer)
├── 语言服务器 (LSP) → 编辑器集成
└── CLI 接口

对比传统方案:

维度Prettier + ESLintBiome
依赖数量Node.js + 两个 npm 包 + 插件单个二进制文件
配置文件.prettierrc + .eslintrc + .eslintignorebiome.json
扫描次数2 次(格式化 + Lint)1 次(biome check
安装方式npm install(需要 Node.js)npm install / curl / brew
CI 时间(大型项目)30-60 秒1-3 秒

2.2 CST:Biome 的秘密武器

大多数 JavaScript 工具使用 AST(抽象语法树)来分析代码。AST 删除了所有「不重要」的语法细节——空格、换行、注释位置——只保留语义信息。

Biome 使用 CST(具体语法树),保留了源代码的所有信息。

这是一个关键的设计决策,原因有三:

原因一:格式化需要精确的源码信息。 Prettier 的做法是「解析→打印」——先用 JavaScript 解析器生成 AST,再用自己的打印机重新输出代码。这意味着 Prettier 丢失了原始格式的所有信息,只能靠启发式规则来「猜测」用户的意图。Biome 的 CST 保留了每一个字符的位置,格式化器可以直接基于 CST 做精确的增量修改。

原因二:Lint 规则需要精确的位置信息。 当 Biome 报告一个错误时,它可以精确指出是哪个字符、哪一行、哪一列。CST 让诊断信息的精度远超基于 AST 的工具。

原因三:CST 支持「格式化 + Lint 一体化」。 因为格式化和 Lint 都基于同一个 CST,Biome 可以在一次解析中同时完成两件事。而 Prettier + ESLint 需要各自独立解析代码各一次。

2.3 性能基准:35 倍是怎么来的

Biome 官方使用 ESLint、Prettier、Webpack 三个大型开源项目作为 benchmark 样本,结果如下:

格式化性能:

  • Biome(多线程):25x 相对速度
  • Biome(单线程):7x
  • Prettier:1x(基准)
  • dprint:1.5-2x

Lint 性能:

  • Biome(多线程):15x
  • Biome(单线程):4x
  • ESLint(基础):1x
  • ESLint(带插件):0.3x

为什么 Rust 能带来如此大的性能差距?三个核心原因:

  1. 内存管理:Rust 的所有权系统消除了垃圾回收(GC)的开销。JavaScript 引擎的 GC 在处理大量 AST 节点时会产生显著的停顿,而 Rust 的内存是编译时确定的。

  2. 并行计算:Rust 的线程模型可以充分利用多核 CPU。Biome 的多线程模式在格式化大型项目时可以利用所有可用核心,而 Node.js 的单线程模型只能使用一个核心。

  3. 零拷贝解析:Biome 的 Lexer 和 Parser 使用零拷贝技术,直接在源代码的字节切片上工作,避免了大量的内存分配和拷贝操作。

2.4 内部模块拆解

让我们深入 Biome 的内部模块:

词法分析器(Lexer)

// 简化的 Biome Lexer 概念
pub struct Lexer<'input> {
    source: &'input str,     // 零拷贝:直接引用源代码
    position: usize,         // 当前位置
    tokens: Vec<Token>,      // 输出的 token 流
}

impl<'input> Lexer<'input> {
    pub fn next_token(&mut self) -> Token {
        // 跳过空白和注释(但记录位置,CST 需要)
        // 识别标识符、关键字、字符串、数字等
        // 返回带有精确位置信息的 Token
    }
}

Lexer 的关键特点是零拷贝:它不分配新的字符串来存储标识符名称,而是直接引用源代码中的字节切片。这在处理大型文件时可以显著减少内存分配次数。

语法解析器(Parser)

Biome 的 Parser 生成 CST 而非 AST。CST 节点包含:

pub struct CstNode {
    kind: NodeKind,           // 节点类型
    range: TextRange,         // 源代码中的精确位置
    slots: Vec<CstElement>,   // 子节点(包括语法Trivia)
}

pub enum CstElement {
    Node(CstNode),            // 语法节点
    Token(Token),             // 终端 Token
    Trivia(Trivia),           // 空白、注释等「非语法」内容
}

Trivia 的保留是 CST 与 AST 的核心区别。格式化器可以通过 Trivia 精确了解用户在代码中放置的空格和注释位置。

格式化器(Formatter)

Biome 的格式化器采用 IR(中间表示)+ 渲染 的两阶段设计:

// 第一阶段:从 CST 生成 IR
pub fn format_node(node: &CstNode) -> FormatElement {
    match node.kind() {
        NodeKind::IfStatement => {
            // 生成 if 语句的格式化 IR
            let condition = format_node(node.condition());
            let body = format_node(node.body());
            
            group![
                "if",
                " ",
                "(",
                indent![soft_line![], condition],
                soft_line![],
                ")",
                " ",
                body,
            ]
        }
        // ... 其他节点类型
    }
}

// 第二阶段:IR 渲染为字符串
pub fn render(element: FormatElement, width: usize) -> String {
    // 根据打印宽度(默认 80 列)决定换行策略
    // group! 元素在超出宽度时展开
    // indent! 元素控制缩进层级
}

这种两阶段设计的好处是:格式化逻辑与输出渲染分离,使得格式化策略更容易测试和维护。

Lint 引擎

Biome 的 Lint 引擎直接在 CST 上运行规则:

// Lint 规则的执行流程
pub fn run_lint_rules(file: &CstFile) -> Vec<Diagnostic> {
    let mut diagnostics = Vec::new();
    
    // 遍历 CST 的每个节点
    for node in file.walk() {
        // 对每个节点运行所有适用的规则
        for rule in get_applicable_rules(node.kind()) {
            if let Some(diag) = rule.check(node) {
                diagnostics.push(diag);
            }
        }
    }
    
    diagnostics
}

因为规则直接在 CST 上运行,Biome 不需要将 CST 转换回 AST,这进一步提升了性能。


第三章:514 条 Lint 规则——从「够用」到「全面覆盖」

3.1 规则分类体系

Biome v2.5 拥有 514 条规则,分为以下几大类:

类别规则数说明
Correctness120+代码正确性检查(必然错误的代码)
Suspicious90+可疑代码模式(可能是 bug)
Style80+代码风格一致性
Complexity40+代码复杂度检查
Performance30+性能优化建议
Security20+安全漏洞检测
A11y30+无障碍访问检查

3.2 亮点规则详解

跨语言规则:noUnusedClasses 和 noUndeclaredClasses

这是 Biome v2.5 的杀手级特性之一。这两条规则通过 Biome 模块图(Module Graph) 实现了跨文件的 CSS 类名检查:

// Button.jsx
export function Button() {
  return <button className="btn btn-primary">Click me</button>;
}

// 如果 "btn-primary" 没有在任何 CSS 文件中定义,
// Biome 会通过 noUndeclaredClasses 规则发出警告:
//
// Button.jsx:2:28 lint/nursery/noUndeclaredClasses
//   ✖ The CSS class btn-primary is not defined in any imported stylesheet.
//   
//   1 │ export function Button() {
//   2 │   return <button className="btn btn-primary">Click me</button>;
//                                      ^^^^^^^^^^^
//   3 │ }
//   
//   i Checked import tree:
//   Button.jsx (this file)
//   └─ imported by: App.jsx -> which imports app.css

这种跨语言的分析能力是 ESLint + Prettier 组合无法实现的,因为 ESLint 无法理解 CSS 文件的语义。

noRestrictedDependencies:基于 e18e 数据的依赖检查

这条规则直接使用了 e18e.dev 项目的数据,检测可被替换的依赖:

// Biome 会检测到 lodash 可以用原生方法替代
import _ from 'lodash';

// ✖ Use native Array.prototype.flat() instead of lodash.flatten
const flat = _.flatten(array);
// ✖ Use native Object.entries() instead of lodash.toPairs
const pairs = _.toPairs(object);

Vue 框架深度支持

Biome v2.5 大幅增强了 Vue 支持,新增了 15+ 条 Vue 专用规则:

  • useVueMultiWordComponentNames:强制组件名使用多单词(防止与 HTML 标签冲突)
  • noVueVIfWithVFor:禁止 v-ifv-for 同时使用(性能陷阱)
  • useVueValidVBind:检查 v-bind 的语法正确性
  • noVueOptionsApi:强制使用 Composition API(可选)

3.3 规则升级机制

Biome 的规则分为三个阶段:

Nursery (实验性) → Stable (稳定) → Recommended (推荐)

Nursery 规则在 biome.json 中需要显式启用:

{
  "linter": {
    "rules": {
      "nursery": {
        "noUnusedClasses": true,
        "noUndeclaredClasses": true
      }
    }
  }
}

当规则经过充分测试和社区反馈后,会被提升为 Stable 规则。v2.5 版本一次性提升了 73 条 Nursery 规则到 Stable,这是一个重要的里程碑。


第四章:实战指南——从零开始使用 Biome

4.1 安装与初始化

# 方式一:通过 npm 安装(推荐)
npm install --save-dev --save-exact @biomejs/biome

# 方式二:通过二进制安装(CI 环境)
curl -fsSL https://biomejs.dev/install.sh | bash

# 方式三:通过 Homebrew 安装(macOS)
brew install biome

# 初始化配置文件
npx @biomejs/biome init

初始化后会生成 biome.json

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 80
  }
}

4.2 核心命令

# 格式化代码
npx @biomejs/biome format --write ./src

# Lint 代码
npx @biomejs/biome lint --write ./src

# 一次性执行格式化 + Lint + 导入排序
npx @biomejs/biome check --write ./src

# CI 环境(只检查不修复,发现错误则退出码非零)
npx @biomejs/biome ci ./src

# 监视模式(v2.5 新增)
npx @biomejs/biome check --write --watch ./src

4.3 从 Prettier + ESLint 迁移

迁移过程非常简单,Biome 提供了自动迁移工具:

# 自动从 Prettier 配置迁移
npx @biomejs/biome migrate prettier

# 自动从 ESLint 配置迁移
npx @biomejs/biome migrate eslint

# 应用迁移后的配置
npx @biomejs/biome check --write ./src

迁移后的 biome.json 会自动包含从 Prettier 和 ESLint 配置中提取的规则。

4.4 CI 集成

GitHub Actions:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: biomejs/setup-biome@v2
        with:
          version: latest
      - run: biome ci ./src

Git Hooks(通过 lint-staged):

// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx,json,css}": "biome check --write --no-errors-on-unmatched"
  }
}

4.5 编辑器集成

Biome 提供了官方的 VS Code 扩展:

# VS Code
code install biomejs.biome

# 或通过命令行
code --install-extension biomejs.biome

VS Code 扩展支持:

  • 保存时自动格式化和 Lint
  • 实时诊断(打字时即时反馈)
  • Quick Fix(一键修复问题)
  • 代码操作菜单

第五章:Biome vs 竞品——2026 年的工具链格局

5.1 Biome vs Prettier + ESLint

维度Prettier + ESLintBiome
安装大小~50MB(含依赖)~15MB(单二进制)
格式化速度1x35x
Lint 速度1x15x
配置复杂度两份配置文件一份 biome.json
规则数量200+(ESLint)514+
跨语言支持CSS/JS 类名检查
框架支持插件生态内置 Vue/React/GraphQL
编辑器集成各自独立统一 LSP
Prettier 兼容性100%(就是它)97%

5.2 Biome vs dprint

dprint 是另一个 Rust 编写的格式化器,但只做格式化,不做 Lint。Biome 的优势在于:

  • 格式化 + Lint 一体化
  • 更丰富的规则库
  • 更好的编辑器集成(LSP)
  • 更活跃的社区和更快的迭代

5.3 Biome vs Ruff(Python 生态的启发)

Ruff 是 Astral 公司用 Rust 编写的 Python Linter/Formatter,它的成功给了 Biome 很多启发:

  • 单二进制分发:无需运行时依赖
  • 极快的性能:比 Flake8 快 10-100 倍
  • 兼容性优先:100% 兼容 Black 格式化输出

Biome 在 JavaScript 生态中扮演着与 Ruff 在 Python 生态中相同的角色。


第六章:GritQL 插件系统——Biome 的扩展性未来

6.1 什么是 GritQL

GritQL 是一种声明式的代码转换语言,允许用户用简单的语法定义自定义的代码模式匹配和转换规则。Biome 在 v2.3 中正式引入了 GritQL 插件支持。

6.2 GritQL 的语法示例

// 将 var 声明替换为 const
`var $x = $y` => `const $x = $y`

// 检测 console.log 调用
`console.log($msg)` => {
  message: "Avoid using console.log in production code"
  severity: "warning"
}

// 自动修复:将 forEach 替换为 for...of
`$arr.forEach(($item) => { $body })` =>
  `for (const $item of $arr) { $body }`

6.3 插件在 Biome 中的使用

// biome.json
{
  "plugins": [
    {
      "name": "my-custom-rules",
      "path": "./gritql-rules/gritql.toml",
      "includes": ["src/**/*.ts"]
    }
  ]
}

v2.5 的新特性允许插件指定 includes 路径,只在特定文件上运行,避免不必要的性能开销。


第七章:性能优化实战——让 Biome 跑得更快

7.1 配置优化

{
  "files": {
    "ignore": ["dist", "node_modules", "build", ".next", "coverage"],
    "ignoreUnknown": true
  },
  "formatter": {
    "lineWidth": 100,
    "indentStyle": "space"
  }
}

7.2 CI 优化

# 使用 biomejs/setup-biome 的缓存功能
- uses: biomejs/setup-biome@v2
  with:
    version: latest
    cache: true  # 启用二进制缓存

7.3 增量模式

Biome v2.5 的 watch 模式支持增量分析,只处理发生变化的文件:

# 开发时使用 watch 模式
npx @biomejs/biome check --write --watch ./src

7.4 性能基准测试

你可以在自己的项目上运行 benchmark:

# 安装 criterion(Rust 的 benchmark 框架)
# 或使用 biome 自带的 benchmark
npx @biomejs/biome benchmark ./src

第八章:Biome 的未来——2026 路线图

根据 Biome 官方的 Roadmap 2026,以下是即将到来的特性:

  1. 类型感知 Lint 规则:利用 TypeScript 的类型信息进行更精确的检查
  2. 更多的跨语言规则:HTML/CSS/JavaScript 之间的深度集成
  3. 性能持续优化:进一步利用 Rust 的 SIMD 和并行特性
  4. 插件生态扩展:GritQL 插件的标准化和社区贡献
  5. 企业级功能:更细粒度的规则配置和报告格式

总结:为什么你应该现在就切换到 Biome

Biome 不是一个「未来可能有用」的工具——它是一个今天就能为你节省时间的工具。

如果你的项目还在用 Prettier + ESLint,Biome 可以:

  • 将 CI 时间从 30 秒缩短到 1 秒
  • 消除两份配置文件的维护负担
  • 获得 514 条高质量的 Lint 规则
  • 享受统一的编辑器体验
  • 获得跨语言的代码质量检查

迁移过程几乎零成本——Biome 提供了自动迁移工具,97% 的 Prettier 兼容性意味着你几乎不需要调整任何格式化规则。

25,000+ 开发者已经做出了选择。Google、Microsoft、Vercel、Cloudflare、Discord 已经在生产环境中验证了 Biome 的可靠性。

是时候告别 Prettier + ESLint 的「双头怪」了。用 Biome,一个工具搞定一切。


附录:常用 Biome 配置模板

React 项目配置

{
  "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
  "organizeImports": { "enabled": true },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedImports": "warn",
        "useExhaustiveDependencies": "warn"
      },
      "suspicious": {
        "noExplicitAny": "warn"
      },
      "style": {
        "useImportType": "error"
      }
    }
  },
  "files": {
    "ignore": ["dist", "node_modules", "build"]
  }
}

Vue 项目配置

{
  "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
  "organizeImports": { "enabled": true },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noVueVIfWithVFor": "error"
      },
      "style": {
        "useVueMultiWordComponentNames": "error",
        "useVueConsistentVBindStyle": "error"
      }
    }
  }
}

本文首发于程序员茄子,转载请注明出处。

推荐文章

38个实用的JavaScript技巧
2024-11-19 07:42:44 +0800 CST
程序员茄子在线接单