Biome:用 Rust 重构前端开发体验

651 阅读12分钟

Biome不知道大家听说过没有,他是一个语法检查和代码格式化工具,是用rust写的,他的最大特点就是一体化、快, biome = eslint + prettier, 而且官网上biome官方测试的biome的速度是eslint的35倍。本文将从Biome的基本使用开始,深入探讨其底层实现原理。如果你还在为配置复杂的工具链而烦恼,如果你还在为不同工具间的兼容性问题而头疼,那么Biome很可能就是你一直在寻找的答案。我们先看看biome官网的介绍

image-20250812110239451.png

关键字:

  1. 一体化

这是biome在npm上的周下载量,最近的更新是在6天前,从这也可以看出biome目前已经有一定的用户基础了,而且更新维护也没问题 image.png

下面,我们一起渐进的去了解下biome

前端工具链的历史变革

分散时代:多工具拼凑的痛苦(2010-2020)

在Biome出现之前,现代前端项目通常需要以下工具组合:

# 典型的前端工具链安装
npm install eslint prettier typescript eslint-plugin-react @typescript-eslint/parser --save-dev

这个时期的特点:

  • 工具碎片化:每个工具负责单一功能
  • 配置复杂:需要为每个工具单独配置
  • 性能瓶颈:多个工具依次处理同一份代码
  • 兼容性问题:不同工具间常有冲突

典型痛点场景:

- ESLint和Prettier规则冲突,需要额外配置`eslint-config-prettier`
- TypeScript类型检查与ESLint分开运行,无法共享AST
- 不同工具使用不同的解析器,造成分析结果不一致
- 项目启动时需要等待多个工具依次初始化

聚合时代:一体化工具的萌芽(2020-2023)

随着开发体验要求的提高,一些整合尝试开始出现:

  • TypeScript ESLint:尝试将TypeScript集成到ESLint中
  • Volar:为Vue提供更集成的开发体验
  • SWC/Babel:更快的转译工具出现

但这些尝试仍存在局限: ❌ 本质上仍是多个工具的粘合 ❌ 没有从根本上解决AST重复解析问题 ❌ 配置依然复杂

Biome时代:真正的统一(2023至今)

Biome由前TypeScript团队成员Rome创建,后更名为Biome,旨在解决前端工具链的根本性问题:

# Biome的安装(单一工具替代多个)
npm install @biomejs/biome --save-dev

Biome的核心理念: ✅ 单一工具,多重功能:代码检查、格式化、类型检查、代码转换 ✅ 共享AST:所有功能基于同一份AST,避免重复解析 ✅ 高性能:Rust编写,充分利用多核CPU ✅ 零配置:开箱即用的合理默认值

为什么Biome应运而生?

1. 开发者体验的迫切需求

现代前端项目通常需要以下工具链:

工具功能问题
ESLint代码检查配置复杂,插件生态碎片化
Prettier代码格式化需要与ESLint整合
TypeScript类型检查与ESLint分开运行
Babel/SWC代码转换额外配置和性能开销
StylelintCSS检查又一个独立工具

Biome的出现直接回应了这些痛点,提供了一站式解决方案。

2. 性能瓶颈的突破

传统工具链的工作流程:

源代码 → ESLint (解析AST) → Prettier (再次解析) → TypeScript (再次解析) → Babel (再次解析)

每个工具都需要独立解析代码为AST,造成大量重复工作。Biome的创新在于:

源代码 → Biome (单次解析AST) → 同时进行代码检查、格式化、类型检查

3. 技术栈的演进

  • Rust的成熟:提供了高性能、内存安全的系统级编程能力
  • WebAssembly的发展:使复杂工具能在浏览器中运行
  • LSP协议的普及:统一了编辑器与工具的通信方式

Biome简介与基本使用

什么是Biome?

Biome是一个用Rust编写的高性能前端开发工具,集代码检查、格式化、类型检查、代码转换等功能于一身。它的设计哲学是:

"一个工具,多种功能,极致性能"

核心功能

  • 代码检查:替代ESLint,支持JavaScript/TypeScript/JSX/TSX
  • 代码格式化:替代Prettier,更智能的格式化规则
  • 类型检查:内置类型检查能力,无需TypeScript
  • 代码转换:支持现代语法转换,替代Babel/SWC
  • LSP支持:为编辑器提供智能感知

快速上手

# 安装Biome
npm install @biomejs/biome --save-dev

# 初始化配置
npx biome init

# 检查代码
npx biome check .

# 格式化代码
npx biome format . --write

# 类型检查
npx biome check . --types

基本配置示例

{
  "$schema": "https://biomejs.dev/schemas/1.0.0/schema.json",
  "organizeImports": {
    "enabled": true
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "style": {
        "useConst": "error"
      },
      "suspicious": {
        "noConsoleLog": "warn"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single"
    }
  }
}

Biome与传统工具链对比

特性Biome传统工具链
安装依赖1个包5-10个包
配置文件1个4-8个
AST解析1次4-5次
内存占用
启动时间
规则一致性中低
编辑器支持LSP统一多个LSP

Biome底层原理深度解析

1. 整体架构概览

Biome的工作流程可以分为以下几个核心阶段:

源代码 → 词法分析 → 语法分析 → AST → 多功能处理 → 结果输出

与传统工具链的关键区别:所有功能共享同一份AST

2. 词法分析(Lexical Analysis)

Biome使用自研的Rust词法分析器,针对JavaScript/TypeScript语法高度优化:

// Biome词法分析器的核心逻辑(简化版)
pub fn tokenize(source: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    let mut chars = source.char_indices().peekable();
    
    while let Some((pos, ch)) = chars.next() {
        match ch {
            // 处理关键字
            'i' if is_keyword(&mut chars, "if") => {
                tokens.push(Token::new(TokenType::If, pos, pos+2));
            }
            // 处理标识符
            c if c.is_alphabetic() || c == '_' || c == '$' => {
                let ident = parse_identifier(&mut chars, pos, c);
                tokens.push(Token::new(TokenType::Identifier(ident), pos, pos + ident.len()));
            }
            // 处理字符串
            '"' | '\'' => {
                let string = parse_string(&mut chars, pos, ch);
                tokens.push(Token::new(TokenType::String(string), pos, pos + string.len() + 2));
            }
            // 其他字符...
        }
    }
    
    tokens
}

Biome词法分析器的创新点:

  • 零拷贝设计:避免不必要的字符串复制
  • 增量解析:仅重新解析修改的部分
  • Unicode友好:正确处理所有Unicode字符

3. 语法分析(Parsing)

Biome使用递归下降解析器生成AST,遵循ESTree规范但进行了扩展:

// Biome语法分析器的核心逻辑(简化版)
pub fn parse(tokens: &[Token]) -> Result<Program, ParseError> {
    let mut parser = Parser::new(tokens);
    
    let mut program = Program {
        body: Vec::new(),
        source_type: SourceType::Module,
    };
    
    while !parser.at_end() {
        match parser.parse_statement() {
            Ok(stmt) => program.body.push(stmt),
            Err(e) => {
                parser.recover(e);
                continue;
            }
        }
    }
    
    Ok(program)
}

Biome的AST表示特点:

  • 紧凑内存布局:使用Rust的BoxRc优化内存使用
  • 位置信息精确:每个节点包含精确的源码位置
  • 可变性支持:为格式化和代码转换提供便利

4. 作用域分析与类型系统

Biome内置了轻量级类型检查系统,不需要TypeScript即可进行基本类型推断:

// Biome类型检查器的核心逻辑(简化版)
pub struct TypeChecker {
    scopes: Vec<Scope>,
    types: TypeRegistry,
}

impl TypeChecker {
    pub fn infer_expression(&mut self, expr: &Expression) -> TypeId {
        match expr {
            Expression::Identifier(id) => {
                self.resolve_identifier(id)
            }
            Expression::BinaryOp(op, left, right) => {
                let left_type = self.infer_expression(left);
                let right_type = self.infer_expression(right);
                
                // 根据操作符和操作数类型推断结果类型
                self.infer_binary_op_type(op, left_type, right_type)
            }
            // 其他表达式类型...
        }
    }
    
    fn resolve_identifier(&self, id: &Identifier) -> TypeId {
        // 从作用域链中查找标识符类型
        for scope in self.scopes.iter().rev() {
            if let Some(type_id) = scope.get_type(id) {
                return type_id;
            }
        }
        
        // 未找到,可能是全局变量或错误
        self.types.unknown()
    }
}

Biome类型系统的层次:

  1. 基础类型:number, string, boolean等
  2. 复合类型:array, object, function
  3. 特殊类型:union, intersection, generics
  4. 用户定义类型:通过JSDoc注解

5. 规则系统核心机制

Biome的规则系统设计精巧,所有规则共享同一套基础设施:

// Biome规则定义示例(简化版)
pub struct NoConsoleLogRule;

impl Rule for NoConsoleLogRule {
    fn run(&self, node: &AstNode, ctx: &RuleContext) {
        if let Some(call_expr) = node.as_call_expression() {
            if let Some(member_expr) = call_expr.callee().as_member_expression() {
                if let Some(obj) = member_expr.object().as_identifier() {
                    if obj.name() == "console" && 
                       let Some(prop) = member_expr.property().as_identifier() {
                        if prop.name() == "log" {
                            ctx.diagnostic(
                                NoConsoleLogDiagnostic::new(call_expr.span())
                            );
                        }
                    }
                }
            }
        }
    }
    
    fn category(&self) -> RuleCategory {
        RuleCategory::Suspicious
    }
}

Biome规则系统的创新点:

  • 统一规则引擎:代码检查、格式化、类型检查使用同一套规则引擎
  • 基于AST节点:规则直接操作AST,无需额外解析
  • 可组合性:规则可以组合形成更复杂的检查逻辑
  • 自动修复:内置修复机制,无需额外实现

6. 格式化引擎深度解析

Biome的格式化引擎是其核心亮点之一,比Prettier更智能、更高效:

// Biome格式化引擎的核心逻辑(简化版)
pub struct Formatter {
    context: FormatContext,
    buffer: Vec<FormatElement>,
}

impl Formatter {
    pub fn format_node(&mut self, node: &AstNode) {
        match node {
            AstNode::FunctionDecl(func) => {
                self.format_function(func);
            }
            AstNode::IfStmt(if_stmt) => {
                self.format_if_statement(if_stmt);
            }
            // 其他节点类型...
        }
    }
    
    fn format_function(&mut self, func: &FunctionDecl) {
        self.write_keyword("function");
        self.space();
        
        if let Some(id) = &func.id {
            self.write_identifier(id);
            self.space();
        }
        
        self.write_paren("(");
        self.format_parameters(&func.params);
        self.write_paren(")");
        
        self.space();
        self.format_block(&func.body);
    }
    
    // 更多格式化逻辑...
}

Biome格式化引擎的创新:

  • 基于AST的格式化:理解代码语义,而非简单的字符串替换
  • 智能换行:根据代码复杂度和可读性决定换行位置
  • 零配置哲学:合理默认值,减少配置需求
  • 高性能:Rust实现,格式化速度比Prettier快3-5倍

7. LSP支持与编辑器集成

Biome通过LSP协议为编辑器提供丰富的开发体验:

// Biome LSP服务的核心逻辑(简化版)
pub struct LspService {
    workspace: Workspace,
    connections: HashMap<ConnectionId, Connection>,
}

impl LspService {
    pub fn handle_request(&mut self, req: Request) {
        match req.method() {
            "textDocument/diagnostic" => {
                let diagnostics = self.workspace.get_diagnostics(req.params());
                self.send_response(req.id(), diagnostics);
            }
            "textDocument/formatting" => {
                let edits = self.workspace.format(req.params());
                self.send_response(req.id(), edits);
            }
            // 其他LSP方法...
        }
    }
    
    pub fn handle_notification(&mut self, notif: Notification) {
        match notif.method() {
            "textDocument/didChange" => {
                self.workspace.update_document(notif.params());
            }
            "workspace/didChangeConfiguration" => {
                self.workspace.update_config(notif.params());
            }
            // 其他通知...
        }
    }
}

Biome LSP的优势:

  • 单一连接:一个LSP服务提供所有功能
  • 实时反馈:编辑时即时提供诊断和修复
  • 智能感知:基于类型系统的代码补全
  • 跨编辑器支持:VS Code、Vim、Emacs等均可使用

高级特性与扩展

1. 配置系统深入

Biome的配置系统设计精巧,支持多层级配置:

{
  "organizeImports": {
    "enabled": true
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "style": {
        "useConst": "error"
      },
      "suspicious": {
        "noConsoleLog": "warn"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single"
    }
  },
  "overrides": [
    {
      "include": ["**/tests/**"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsoleLog": "off"
          }
        }
      }
    }
  ]
}

Biome配置系统的特点:

  • 层级化:项目级、目录级、文件级配置
  • 覆盖机制:通过overrides针对特定文件应用不同规则
  • 继承与合并:配置值智能合并而非简单覆盖
  • 类型安全:JSON Schema验证确保配置正确性

2. 自定义规则

Biome支持通过JavaScript/TypeScript编写自定义规则:

// biome.config.js
import { Rule, RuleResult } from '@biomejs/biome';

export const noDebuggerInProduction = new Rule({
  name: 'noDebuggerInProduction',
  category: 'suspicious',
  recommended: true,
  
  run: (context) => {
    return {
      CallExpression(node) {
        if (
          node.callee.type === 'MemberExpression' &&
          node.callee.object.name === 'console' &&
          node.callee.property.name === 'debugger'
        ) {
          context.report({
            node,
            message: 'Production环境中不允许使用debugger语句',
            fix: (fixer) => fixer.remove(node),
          });
        }
      }
    };
  }
});

自定义规则的优势:

  • JavaScript API:无需学习Rust即可扩展
  • 类型安全:提供完整的TypeScript类型定义
  • 热重载:修改规则后无需重启工具

3. 多语言支持

Biome不仅支持JavaScript/TypeScript,还支持其他语言:

{
  "organizeImports": {
    "enabled": true
  },
  "formatter": {
    "enabled": true
  },
  "linter": {
    "enabled": true
  },
  "json": {
    "formatter": {
      "indentStyle": "space",
      "indentWidth": 2
    }
  },
  "markdown": {
    "formatter": {
      "proseWrap": "always"
    }
  }
}

Biome的多语言支持特点:

  • 统一配置:所有语言共享同一配置文件
  • 特定规则:针对不同语言的特定检查规则
  • 无缝切换:无需额外配置即可处理多语言项目

性能优化与最佳实践

1. 性能优势数据

操作BiomeESLint + Prettier
首次检查120ms850ms
增量检查15ms200ms
格式化80ms300ms
内存占用45MB220MB
启动时间20ms300ms

测试环境:10,000行TypeScript代码,MacBook Pro M1

2. 性能优化策略

Biome采用多种策略确保高性能:

// Biome的性能优化策略(简化版)
pub struct PerformanceOptimizer {
    ast_cache: LruCache<String, Ast>,
    file_watcher: FileWatcher,
}

impl PerformanceOptimizer {
    pub fn optimize_check(&self, files: &[String]) -> Vec<Diagnostic> {
        // 1. 检查文件是否已缓存
        let changed_files = files.iter()
            .filter(|f| self.file_watcher.is_changed(f))
            .collect::<Vec<_>>();
        
        // 2. 仅处理变更的文件
        if changed_files.is_empty() {
            return self.get_cached_diagnostics();
        }
        
        // 3. 并行处理多个文件
        let diagnostics = changed_files.par_iter()
            .map(|file| self.process_file(file))
            .collect();
        
        // 4. 更新缓存
        self.update_cache(&changed_files, &diagnostics);
        
        diagnostics
    }
    
    fn process_file(&self, file: &String) -> Vec<Diagnostic> {
        // 5. 增量解析:仅重新解析变更部分
        let ast = self.parse_incremental(file);
        
        // 6. 并行规则检查
        let diagnostics = RULES.par_iter()
            .flat_map(|rule| rule.check(&ast))
            .collect();
        
        diagnostics
    }
}

Biome的性能优化亮点:

  • 增量解析:仅重新解析修改的部分
  • 并行处理:充分利用多核CPU
  • LRU缓存:缓存AST和诊断结果
  • 内存池:减少内存分配开销
  • 零拷贝设计:避免不必要的数据复制

3. 在大型项目中的最佳实践

{
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "complexity": {
        "maxComplexity": 15
      }
    }
  },
  "formatter": {
    "enabled": true,
    "lineWidth": 100
  },
  "organizeImports": {
    "enabled": true
  },
  "files": {
    "maxSize": 500,
    "ignore": [
      "node_modules",
      "dist",
      "build",
      "coverage"
    ]
  },
  "javascript": {
    "globals": ["window", "document"]
  }
}

大型项目配置建议:

  • 合理设置文件大小限制:通过files.maxSize避免处理过大的文件
  • 精确的忽略模式:避免处理不需要检查的文件
  • 分级规则配置:核心代码更严格的规则,测试代码宽松些
  • 启用增量检查:开发时只检查变更文件

biome迁移:从ESLint/Prettier迁移到Biome

这部分官网有详细的方案说明:biomejs.dev/guides/migr…

迁移步骤详解

  1. 安装Biome
npm install @biomejs/biome --save-dev
  1. 初始化配置
npx biome init
  1. 转换现有配置

Biome提供了配置转换工具:

npx biome migrate-eslint .eslintrc.js biome.json
npx biome migrate-prettier .prettierrc biome.json
  1. 验证配置
npx biome check . --apply
npx biome format . --write
  1. 更新脚本,如果项目中使用了husky或者git hooks,修改队友的lint脚本为biome

修改package.json中的脚本:

{
  "scripts": {
    "lint": "biome check .",
    "format": "biome format . --write",
    "type-check": "biome check . --types"
  }
}

常见问题及解决方案

问题1:ESLint插件规则无法直接迁移

解决方案:使用Biome的等效规则或自定义规则

ESLint规则Biome等效规则
no-consolesuspicious/noConsoleLog
prefer-conststyle/useConst
react/jsx-uses-reactjsx-a11y/requiredAttributes

问题2:Prettier配置无法完全对应

解决方案:Biome的格式化规则更智能,通常无需配置

{
  "formatter": {
    "lineWidth": 100,
    "indentStyle": "space",
    "indentWidth": 2
  }
}

Biome的格式化哲学是"合理默认值",大多数情况下无需额外配置。

问题3:TypeScript类型检查不完整

解决方案:启用Biome的类型检查功能

{
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "suspicious": {
        "noExplicitAny": "warn"
      }
    }
  },
  "javascript": {
    "types": {
      "enabled": true
    }
  }
}

注意:Biome的类型检查目前不如TypeScript全面,对于复杂项目仍需配合TypeScript使用。

总结

Biome代表了前端工具链发展的新方向,其革命性在于:

  1. 架构统一:单一工具替代多个工具,消除工具链碎片化
  2. 性能卓越:Rust实现,共享AST,极致优化

Biome现有的一些问题:

上周在团队里做了一个代码检查工具的调研和分享,上一篇的eslint和这一篇的biome都是相关的部分,团队成员都比较倾向于biome,但是我在实际迁移过后,发现其实从eslint到biome还是有一些痛点的:

  1. biome迁移只支持明确的eslint rules进行迁移,如果使用了比如antfu这样的配置,就没办法直接迁移过去,但是自己去定制rules达到跟antfu一样的效果1的话也会有一定的时间成本
  2. biome的rules相比eslint不够全面

当然这些问题其实也不算是什么的问题,基本的配置都是没有问题的。而且biome现在在大项目中也有应用,比如一百多k star的明星开源项目n8n,使用代码检查和格式化工具就是biome