概述
本文是笔者的系列博文 《Bun技术评估》 中的第二十篇。
在本文的内容中,笔者主要想要来探讨一下Bun中的对于YAML支持的问题。
在Bun技术文档中,有专门的YAML版块和章节如下:
什么是YAML
应当承认,笔者在之前的开发工作中,对于YAML的使用和了解的机会并不多,只是大概的了解到它是一种配置信息的表达和存储格式,而且现在使用的好像也愈来愈广泛(比如Docker)。笔者在日常工作中,通常直接使用JSON甚至是JS来编写和使用配置信息,虽然到现在为止也没有觉得有什么不便,但确实觉得有点奇怪,程序执行和数据类型来作为配置信息的存储和表达。这里刚好有机会能够更加深入的理解和体会这种方式。
YAML的全称是:YAML Ain't Markup Language(YAML 不是一种标记语言)。这很幽默,但好像逻辑上有点问题,好像有点自证循环了。当然名字并不是很重要,从命名的来源来看,它就是为了改善XML语言而设计的。实际情况也是如此,它的设计目标是不使用标记语言的语法,来实现很强的可读性和数据系列化。主要用于替代XML\JSON这类结构化数据格式,让配置文件更简洁、更容易阅读。
为了理解这一点,我们先粗略的从其语法规则入手。总结一下就是以下几点:
- 
- 使用缩进(通常为两个空格)表示层级关系
 
这和Python的语法有点像:
            
            
              YAML
              
              
            
          
          app:
  name: MyApp
  version: 1.0相当于 JSON:{ app : { name : "MyApp", version: "1.0" }}
- 
- 使用冒号分隔的键值对,表示对象属性和值,又类似于JSON
 
- 
- 使用 - 表示列表的成员(数组)
 
            
            
              js
              
              
            
          
          servers:
  - host: 127.0.0.1
    port: 8080
  - host: 127.0.0.2
    port: 9090- 
- 支持多行字符串(|符号)
 
            
            
              js
              
              
            
          
          description: |
  这是一个多行字符串。
  保留换行符。这时这个配置信息会保留换行符。
- 
- 支持引用和锚点,即复用配置信息
 
这个特性非常强大和实用啊!
            
            
              js
              
              
            
          
          default: &default
  timeout: 30
  retries: 3
api:
  <<: *default
  endpoint: /api- 
- 支持注释(#)
 
这是JSON的一个主要的限制和不便。#符号可以用在整行和行中的右侧注释。
            
            
              js
              
              
            
          
          # 这是一个注释
name: MyApp  # 这一行的右侧也是注释
version: 1.0- 
- 多文档 "---"
 
在单一YAML文件中,可以使用"---"符号,分隔和直接定义多个顶层对象,其解析后的形式是对象数组。
            
            
              yaml
              
              
            
          
          ---
name: Document 1
---
name: Document 2
---
name: Document 3- 
- 支持标量数据类型,包括 string, number,boolers,null等等
 
还可以通过指令,配置类型限制和转换:
            
            
              js
              
              
            
          
          config: !!str 123  # Explicit string type这个特性,如果合理使用,可以节省很多转换操作,并避免一些由于数据类型产生的错误。
- 
- YAML指令(Directives),包括YAML和TAG等
 
指令就是yaml的元数据信息,它们一般位于 YAML 文档的开头,用于告诉解析器如何解释文档内容。如YAML指令是一个版本声明:
            
            
              YAML
              
              
            
          
          %YAML 1.2
---
title: "版本声明示例"TAG指令稍微复杂一点,用于定义一个标签前缀(tag handle)与实际的命名空间 URI之间的映射。 这样可以在文档中用简短的标签来引用更复杂的类型。
            
            
              YAML
              
              
            
          
          %TAG !cfg! tag:example.com,2025:config/
---
app_name: !cfg!MyApp
timeout: !cfg!30s笔者的理解,所谓TAG,本质上就是一个变量声明,可以在实际的配置信息中引用。但和普通的值字符串不一样,这个值是带"标签的",也就是说,如果解析器支持标签解析,它会将这个值,进一步的解析成为{ tag, value } 对象。后面有示例详细讨论。
从上面的特性列表来看,我们应该可以确实的感觉到,YAML的组织、表达和扩展能力,确实是比XML和JSON强大和简洁,特别是弥补了很多JSON的不足。笔者也原意在后续的开发中,引入和使用这种配置模式(还有一个重要的原因就是Bun的内置支持)。
YAML的主要用途和场景包括:
- 
配置文件(如 docker-compose.yml, GitHub Actions, Kubernetes 等) 
- 
数据交换(与 JSON 类似) 
- 
定义结构化信息(如 CI/CD 流程、环境变量、应用配置) 
笔者的理解,可能是由于YAML更加技术中立和语言无关,所以它被广泛的应用于一些通用的系统配置信息领域,所以得到了更广泛的认可和使用。
Bun的YAML实现
Bun提供了内置的YAML的解析和处理实现,无需第三方库和模块,其支持的YAML版本为1.2。按照其技术文档的说法,Bun的YAML解析器通过了超过90%的YAML官方测试用例,而且正在积极努力以实现100%的兼容性。当前的实现已经覆盖了绝大多数实际使用场景。解析器使用Zig编写来提供更好的性能,并在持续改进中。
Bun的YAML模块主要有以下几种使用方式:
YMAL文件
当然,作为配置信息,可能最常用的使用方式,就是加载和使用YAML配置文件了。Bun提供了非常方便和易于理解的YAML配置加载方式。最令人舒适的是,使用方式就和一般JS对象结构无异。
            
            
              js
              
              
            
          
          // config.yaml 定义配置
database:
  host: localhost
  port: 5432
  name: myapp
redis:
  host: localhost
  port: 6379
features:
  auth: true
  rateLimit: true
  analytics: false
// 文件导入对象
import config from './config.yaml';
console.log(config.database.host); // "localhost"
console.log(config.redis.port); // 6379
// 命名变量导入
import {database, redis, features} from './config.yaml';
console.log(database.host); // "localhost"
console.log(redis.port); // 6379
console.log(features.auth); // true
// 复合导入
import config, {database, features} from './config.yaml';
// Common JS require方式
const config = require('./config.yaml');
console.log(config.database.name); // "myapp"
// Destructuring also works
const {database, redis} = require('./config.yaml');
console.log(database.port); // 5432Hot Load 热加载
我们应该已经了解到,Bun有一个很好的特性,就是热加载(Hot Load)。现在它也把这个特性应用到YAML的支持当中了。就是如何使用 bun --hot 来运行应用程序时,对外部配置的YAML文件的修改会被自动检测并重新加载,而且无需关闭现有的程序和连接,一般也不会影响到无关的程序状态。
下面是一个简单的示例:
            
            
              js
              
              
            
          
          // YAML配置信息
server:
  port: 3000
  host: localhost
features:
  debug: true
  verbose: false
// 程序使用
import {server, features} from './config.yaml';
console.log(`Starting server on ${server.host}:${server.port}`);
if (features.debug) {
   console.log('Debug mode enabled');
}
..现在,如果修改 config.yaml 时,变更会立即反映到正在运行的应用程序中。这非常适合于在开发过程中动态调整配置,无需重启应用来测试不同设置,实时调试并观察配置变化效果或者切换功能开关(Feature Flag)等各种适合的需求和场景。
import/require YAML
除了直接使用YAML文件之外,作为基础模块,可以从bun全局对象中,引入和使用YAML:
            
            
              js
              
              
            
          
          import {YAML} from 'bun';其实,这个操作,可能都不是必须的,因为可以直接引用bun.YAML对象,在后面的例子中可以看到。
Parse 解析
基于YAML对象和其方法,可以解析YAML字符串到json对象,就和JSON对象一样。
            
            
              js
              
              
            
          
          import {YAML} from 'bun';
const yaml = `
# Employee record
employee: &emp
  name: Jane Smith
  department: Engineering
  skills:
    - JavaScript
    - TypeScript
    - React
manager: *emp  # Reference to employee
config: !!str 123  # Explicit string type
description: |
  This is a multi-line
  literal string that preserves
  line breaks and spacing.
summary: >
  This is a folded string
  that joins lines with spaces
  unless there are blank lines.
`;
const data = YAML.parse(yaml);
// 但是要注意错误处理
try {
     let config = Bun.YAML.parse('invalid: yaml: content:');
} catch (error) {
    console.error('Failed to parse YAML:', error.message);
}Tag解析
这里特别说明一下,虽然YAML的TAG特性很不错,但现阶段Bun并不支持这个特性。
如果能够支持,大体的情况应该如下:
            
            
              YAML
              
              
            
          
          // YAML文件内容
%YAML 1.2
%TAG !sec! tag:security:secret/
---
server 
  password: !sec!AES256 "U2FsdGVkX1+..."
// 解析代码
const c = require("./config.yaml");
console.log("Config Info:",c);
{
  server: { password: "!sec!AES256 U2FsdGVkX1+..." }
}
// 带有Tag解析器,解析的结果
{ server:  
  { password: {
        tag: "tag:security:secret/",
        value :"xxxx..."
    }
  }    
}多文档(对象)
在顶层可以定义和解析多个对象信息,并组织称为数组方式。
            
            
              js
              
              
            
          
          const multiDoc = `
---
name: Document 1
---
name: Document 2
---
name: Document 3
`;
const docs = Bun.YAML.parse(multiDoc);
console.log(docs);
// [
//   { name: "Document 1" },
//   { name: "Document 2" },
//   { name: "Document 3" }
// ]包器集成(Bundler Integration)
这看起来应该是一个前端的特性。笔者理解,就是它能够在打包阶段,自动处理YAML内容。
bun build app.ts --outdir=dist
如上面的指令,可以在编译时,将YAML构建成为一Javascript模块。这样操作的好处在于:
- 环境没有运行时 YAML 解析开销
- 应用程序无需YAML相关支持代码
- 体积更小
- 对未使用的配置进行 Tree-shaking(按需导入)
小结
在本文中,笔者研究和探讨了Bun中内置YAML支持相关的内容。包括YAML的基本概念和模式,主要特性,以及在Bun中时如何对其进行支持的,包括了典型的使用场景和示例代码。