Bun技术评估 - 20 YAML

概述

本文是笔者的系列博文 《Bun技术评估》 中的第二十篇。

在本文的内容中,笔者主要想要来探讨一下Bun中的对于YAML支持的问题。

在Bun技术文档中,有专门的YAML版块和章节如下:

bun.com/docs/runtim...

什么是YAML

应当承认,笔者在之前的开发工作中,对于YAML的使用和了解的机会并不多,只是大概的了解到它是一种配置信息的表达和存储格式,而且现在使用的好像也愈来愈广泛(比如Docker)。笔者在日常工作中,通常直接使用JSON甚至是JS来编写和使用配置信息,虽然到现在为止也没有觉得有什么不便,但确实觉得有点奇怪,程序执行和数据类型来作为配置信息的存储和表达。这里刚好有机会能够更加深入的理解和体会这种方式。

YAML的全称是:YAML Ain't Markup Language(YAML 不是一种标记语言)。这很幽默,但好像逻辑上有点问题,好像有点自证循环了。当然名字并不是很重要,从命名的来源来看,它就是为了改善XML语言而设计的。实际情况也是如此,它的设计目标是不使用标记语言的语法,来实现很强的可读性和数据系列化。主要用于替代XML\JSON这类结构化数据格式,让配置文件更简洁、更容易阅读。

为了理解这一点,我们先粗略的从其语法规则入手。总结一下就是以下几点:

    1. 使用缩进(通常为两个空格)表示层级关系

这和Python的语法有点像:

YAML 复制代码
app:
  name: MyApp
  version: 1.0

相当于 JSON:{ app : { name : "MyApp", version: "1.0" }}

    1. 使用冒号分隔的键值对,表示对象属性和值,又类似于JSON
    1. 使用 - 表示列表的成员(数组)
js 复制代码
servers:
  - host: 127.0.0.1
    port: 8080
  - host: 127.0.0.2
    port: 9090
    1. 支持多行字符串(|符号)
js 复制代码
description: |
  这是一个多行字符串。
  保留换行符。

这时这个配置信息会保留换行符。

    1. 支持引用和锚点,即复用配置信息

这个特性非常强大和实用啊!

js 复制代码
default: &default
  timeout: 30
  retries: 3

api:
  <<: *default
  endpoint: /api
    1. 支持注释(#)

这是JSON的一个主要的限制和不便。#符号可以用在整行和行中的右侧注释。

js 复制代码
# 这是一个注释
name: MyApp  # 这一行的右侧也是注释
version: 1.0
    1. 多文档 "---"

在单一YAML文件中,可以使用"---"符号,分隔和直接定义多个顶层对象,其解析后的形式是对象数组。

yaml 复制代码
---
name: Document 1
---
name: Document 2
---
name: Document 3
    1. 支持标量数据类型,包括 string, number,boolers,null等等

还可以通过指令,配置类型限制和转换:

js 复制代码
config: !!str 123  # Explicit string type

这个特性,如果合理使用,可以节省很多转换操作,并避免一些由于数据类型产生的错误。

    1. 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); // 5432

Hot 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中时如何对其进行支持的,包括了典型的使用场景和示例代码。

相关推荐
码事漫谈6 小时前
AI时代之我见:架构师如何与AI共生
后端
码事漫谈6 小时前
用 Gitea 给 StackEdit 搭一个「图床」- 详细步骤截图
后端
千码君20167 小时前
Go语言:常量计数器iota的意义
开发语言·后端·golang·状态码·const·iota·常量
IT_陈寒8 小时前
Python开发者必看:这5个鲜为人知的Pandas技巧让你的数据处理效率提升50%
前端·人工智能·后端
豆苗学前端8 小时前
写给女朋友的第一封信,测试方法概论
前端·后端·设计模式
武子康8 小时前
大数据-140 ClickHouse CollapsingMergeTree详解 外部数据源最小闭环HDFS/MySQL/Kafka
大数据·后端·nosql
m0_736927048 小时前
使用 Python 将 PowerPoint 转换为 Word 文档
java·开发语言·后端·职场和发展·c#
杜子不疼.8 小时前
【Rust】路由匹配与参数提取:从 match 语句到 axum 的类型魔法
开发语言·后端·rust
qq_12498707539 小时前
基于Flask的穷游网酒店数据分析系统(源码+论文+部署+安装)
后端·python·flask·毕业设计