needhelp
← 返回博客

Astro 6.4 深度解析:可插拔 Markdown 流水线、Rust 驱动的 Sätteri 与 Cloudflare 部署革命

作者 needhelp
Astro
前端
Rust
Cloudflare
Markdown
SSG
Web 开发

2026 年 5 月 28 日,Astro 发布 6.4。这既不是常规 feature bump,也不是简单 bugfix 合辑——它是一个结构拐点。

三个核心变化各自切开一条深层趋势线:

  • Markdown 处理器接口化——告别 unified 的十年垄断
  • Sätteri——一个从零开始用 Rust 写的 Markdown/MDX 处理器,CI 构建时间从 120 秒砍到 55 秒
  • cf() 辅助函数——把 Cloudflare 上的 6+ 个绑定和上下文注入压缩成一行

往下拆。


一、Unified 垄断的终结

1.1 历史遗留

Astro 从诞生第一天起,Markdown 管线就死死绑定在 unified 生态上——具体说是 remark(解析 Markdown AST)+ rehype(转换 HTML AST)及其上千个插件。这本身不是问题——unified 生态庞大、灵活。问题是它是硬编码的

你没法换掉它。哪怕你的场景只需要 GFMD 和标题锚点,整个 remark→rehype→stringify 的 JS 管线也得完整跑一遍。

6.4 的 markdown.processor API 把这个管线从固定依赖变成了可替换接口

1.2 架构变化

graph TD
    subgraph "6.4 之前:硬编码管线"
        A1[astro.config] -->|固定| B1[Unified 引擎]
        B1 --> C1[remarkPlugins]
        B1 --> D1[rehypePlugins]
        C1 --> E1[Markdown AST]
        D1 --> E1
    end

    subgraph "6.4+:可插拔管线"
        A2[astro.config] -->|markdown.processor| B2[Processor 接口]
        B2 --> C2[Unified<br/>默认处理器]
        B2 --> D2[Sätteri<br/>Rust 处理器]
        B2 --> E2[自定义引擎]
        C2 --> F2[JS 插件生态]
        D2 --> G2[原生 Rust 管线]
        E2 --> H2[用户定义的 AST]
    end

    style A1 fill:#ffcccc
    style A2 fill:#ccffcc
    style B2 fill:#e1f5fe

核心变化在于:astro.config 不再直接接受 remarkPlugins / rehypePlugins 这类顶层配置。取而代之的是一个统一的 processor() 调用。

1.3 新配置怎么写

老的写法在 6.4 里仍然能跑,但已经被标记为废弃,将在 Astro 8.0 移除:

// ❌ 废弃(6.4 兼容,8.0 删除)
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
remarkPlugins: ['remark-toc'],
rehypePlugins: ['rehype-slug'],
smartypants: true,
gfm: true,
},
});

新的写法:

// ✅ Astro 6.4+ 推荐
import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
export default defineConfig({
markdown: {
processor: unified({
remarkPlugins: [remarkToc],
rehypePlugins: [rehypeSlug],
smartypants: true,
gfm: true,
}),
},
});

改动很小,但架构含义很大——现在所有 Markdown 配置都收在一个 processor 调用里,随时可以整块换成 Sätteri 或其他引擎。

1.4 废弃时间线

从 6.4 到 8.0 的窗口大约是 12-18 个月。越晚迁移,升级断层越痛:

[ \text{技术债务风险} = \int_{t_{6.4}}^{t_{8.0}} \text{配置膨胀度}(t) , dt ]

如果你的项目同时在加新插件和加新页面,累计债务会超线性增长。建议从现在就开始清理。


二、Sätteri:Rust 进入 Markdown 管线

2.1 它是什么

@astrojs/markdown-sätteri 是一个从零重写的 Rust Markdown/MDX 处理器。它不是 unified 的 Rust 加速版——它有自己的 AST 规范、自己的解析器、自己的序列化器。这意味着它不是跑 remark 插件更快,而是干脆不跑 remark 插件。

2.2 性能实测

Astro 团队在两个真实站点上做了基准测试:

站点Unified (基线)Sätteri提速倍数
Astro 官方文档站142s63s2.25×
Cloudflare 文档站120s55s2.18×
中型营销站点38s22s1.73×

Sätteri 的提速在大规模文档站点上最显著。原因很直接——unified 管线中的每个插件都是一次全 AST 遍历,插件越多遍历次数越多。Sätteri 把常见的 GFM 特性(表格、任务列表、自动链接、删除线)做成编译期选项,一次遍历完成:

xychart-beta
    title "构建时间对比:Unified vs Sätteri"
    x-axis ["Unified (基线)", "Sätteri (Rust)"]
    y-axis "构建时间 (秒)" 0 --> 150
    bar [120, 55]

对于 CI/CD 场景,累积节省可以这样算:

[ \text{总节省} = n_{\text{日构建次数}} \times \Delta T \times d_{\text{工作日}} ]

日构建 50 次 × 每次 65 秒 = 每天省 54 分钟。一年 ≈ 230 小时 CI 时间。

2.3 但兼容性是硬伤

Sätteri 不兼容 remark/rehype 插件。这不是 bug——这是 Rust AST 管线的架构必然。MDAST(Markdown AST)和 HAST(HTML AST)是 JavaScript 数据结构,Rust 原生管线没法直接执行 JS 插件:

graph LR
    subgraph "插件兼容矩阵"
        direction TB
        P1[remark-toc] -->|❌ 不支持| S[Sätteri]
        P2[remark-gfm] -->|✅ 原生支持| S
        P3[rehype-slug] -->|❌ 不支持| S
        P4[rehype-autolink-headings] -->|❌ 不支持| S
        P5[自定义 remark 插件] -->|⚠️ 需要移植| S
        P6[自定义 rehype 插件] -->|⚠️ 需要移植| S
    end

    style S fill:#fff3e0
    style P2 fill:#e8f5e9
    style P1 fill:#ffebee
    style P3 fill:#ffebee
    style P4 fill:#ffebee

Astro 6.4 的路线图明确说 Sätteri 会在未来大版本成为默认处理器。这意味着你现在有两个选择:

  1. 现在就评估并移植——如果插件依赖少,可以直接切
  2. 留在 unified 上等生态成熟——但要在 8.0 之前完成迁移

决策公式:

[ \text{净收益} = \alpha \cdot \text{速度增益} - \beta \cdot \text{插件迁移成本} ]

文档站点(少插件,多内容):(\alpha \gg \beta),立即切划算。 重度插件博客(toc + slug + autolink + math + diagram):(\beta) 可能盖过 (\alpha)。

2.4 Sätteri 原生支持什么

功能UnifiedSätteri备注
GFM(表格、任务列表等)✅ 插件✅ 原生免费获得
Smartypants(智能引号)✅ 插件✅ 原生免费获得
directive 语法⚠️ 需要 remark-directive✅ 原生 features: { directive: true }更简洁
MDAST/HAST 插件✅ 全部核心限制
自定义组件✅ MDX✅ MDXSätteri 支持 MDX
数学公式⚠️ 需要 remark-math❌ 需 unified 回退混合模式可行

三、Cloudflare 部署:六个绑定压缩为一个

3.1 过去的手工劳动

在 6.4 之前,在 Astro 中部署到 Cloudflare 需要手动处理:

// ❌ 6.3 及之前 —— 每个绑定都要手动注入
export async function onRequest(context) {
const { request, env, ctx } = context;
const sessionKV = env.SESSION_KV;
const assets = env.ASSETS;
const clientIP = request.headers.get('cf-connecting-ip');
const waitUntil = ctx.waitUntil.bind(ctx);
// 然后才能接触到 Astro 的请求处理
return await handleRequest(request, {
sessionKV, assets, clientIP, waitUntil
});
}

有六个常见绑定和上下文需要注入:SESSION KV、ASSETS、cf-connecting-ip、waitUntil、locals.cfContext、错误页面路由。少一个就可能在生产中出现奇怪的 500。

3.2 cf() 的抽象

cf(state, env, ctx) 把这六个压缩成一个调用:

sequenceDiagram
    autonumber
    participant C as 客户端
    participant F as Fetch Handler
    participant CF as cf(state, env, ctx)
    participant KV as SESSION KV
    participant AS as ASSETS
    participant IP as cf-connecting-ip
    participant WU as waitUntil
    participant A as Astro 渲染

    C->>F: HTTP 请求
    F->>CF: 调用 cf() 辅助函数
    CF->>KV: 注入 KV 绑定
    CF->>AS: 解析静态资源
    CF->>IP: 提取真实客户端 IP
    CF->>WU: 注册后台任务
    alt 命中静态资源
        CF-->>F: 返回资源
        F-->>C: 200 OK + 资源
    else 需要渲染
        CF->>A: 转发到 Astro
        A-->>F: HTML 响应
        F-->>C: 200 OK + HTML
    end

这是现在实际配置的代码:

// ✅ Astro 6.4+
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'server',
adapter: cloudflare({
advancedRouting: {
cf: true, // 一行启用 cf() 辅助
},
}),
});

advancedRouting.cf: true 会自动注入所有绑定。不需要手动拼接上下文。

3.3 Hono 中间件集成

对使用 Hono 的团队,cf() 暴露为 Hono 中间件:

import { Hono } from 'hono';
import { cf } from '@astrojs/cloudflare/hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
const app = new Hono<{ Bindings: Env }>();
app.use(cf()); // ← 一行注入所有 Cloudflare 绑定
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());
export default app;

抽象后的接口复杂度:

[ \text{集成复杂度}{之前} = \sum{i=1}^{6} \text{绑定}_i \times \text{模板代码}i ] [ \text{集成复杂度}{之后} = 1 \times \text{cf()} ]

换句话说,绑定数越多,cf() 的简化效果越明显。如果你的项目只用了一个 KV 绑定,收益有限。但如果用到了 KV + D1 + R2 + Queue + AI Gateway,那这个抽象值是巨大的。

3.4 开发-生产一致性

一个隐晦但重要的改进:6.4 中本地 wrangler 开发服务器与 Cloudflare Edge 运行时的行为更接近了。之前常见的 bug 类别——本地正常、上线挂了——很大一部分来自绑定解析差异:

flowchart TB
    subgraph "6.4 之前"
        D1[本地开发] -->|行为分歧| P1[Cloudflare Edge]
        D1 -->|Bug 只能在线上发现| D1
        style D1 fill:#ffebee
        style P1 fill:#ffebee
    end
    
    subgraph "Astro 6.4+"
        D2[本地开发<br/>wrangler + cf()] -->|高保真| P2[Cloudflare Edge]
        style D2 fill:#e8f5e9
        style P2 fill:#e8f5e9
    end

具体来说,以下差异在 6.4 中显著缩小:

  • KV 命名空间解析路径与线上一致
  • 静态资源 ASSETS 绑定行为同步
  • cf-connecting-ip 在本地有了模拟值
  • 错误页面路由不再需要手动配置

四、安全升级路径

4.1 三阶段迁移

Astro 6.4 的升级策略可以拆成三个阶段:

flowchart LR
    A[阶段一:升级 CLI] -->|npx @astrojs/upgrade| B[阶段二:更新配置]
    B -->|wrangler.jsonc<br/>单入口点| C[阶段三:审计与测试]
    C -->|检查 Markdown 渲染<br/>验证插件兼容性| D[投入生产]

    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style D fill:#f3e5f5

阶段一:升级

Terminal window
npx @astrojs/upgrade
# 或
bunx @astrojs/upgrade

这会自动处理版本号更新和依赖重装。如果你用了 @astrojs/cloudflare,它也会升级适配器。

阶段二:配置迁移

Terminal window
# 验证新配置文件格式
npx astro sync

如果你的项目用了 src/env.d.ts,Astro 6.4 建议迁移到 src/env.d.ts 中新的类型声明:

/// <reference types="astro/client" />
/// <reference types="@astrojs/cloudflare" />

wrangler.jsonc 的配置:

{
"name": "my-astro-site",
"compatibility_date": "2026-05-28",
"compatibility_flags": ["nodejs_compat"],
"pages_build_output_dir": "./dist"
}

阶段三:审计清单

检查项Unified 路径Sätteri 路径
构建时间基准线~50% 更快
remarkPlugins✅ 正常工作❌ 需要移植
rehypePlugins✅ 正常工作❌ 需要移植
gfm✅ 插件支持✅ 原生支持
smartypants✅ 插件支持✅ 原生支持
directive 语法❌ 需要插件✅ 原生 (features: { directive: true })

4.2 迁移决策矩阵

quadrantChart
    title "Sätteri 迁移策略矩阵"
    x-axis 低插件依赖 --> 高插件依赖
    y-axis 低构建时间敏感度 --> 高构建时间敏感度
    quadrant-1 "立即迁移"
    quadrant-2 "评估与移植"
    quadrant-3 "留在 Unified"
    quadrant-4 "先做基准测试"
    "文档站点": [0.2, 0.9]
    "营销博客": [0.4, 0.6]
    "重度插件博客": [0.8, 0.3]
    "电商内容页": [0.6, 0.7]
    "技术教程站": [0.3, 0.85]
    "企业官网": [0.5, 0.4]

落在这个矩阵左上角的项目(文档站点、技术教程站)——插件少、构建时间长——立即迁移收益最大。落在右下角的(重度插件博客、高度定制的营销站)——可以先做基准测试,等插件生态成熟再切。

4.3 混合使用的逃逸阀

如果你的项目既需要 Sätteri 的速度又离不开某些 remark 插件,有一个变通方案——按目录配置:

import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import { sätteri } from '@astrojs/markdown-sätteri';
export default defineConfig({
markdown: {
processor: unified(),
// 对特定内容集合使用 Sätteri
contentCollections: {
docs: { processor: sätteri() },
blog: { processor: unified() }, // 保留插件支持
},
},
});

这个功能还在实验阶段(需要 experimental.contentCollectionProcessorRouting: true),但它给出了一个务实的中间路径:重度插件的内容用 unified,高性能需求的内容用 Sätteri。


五、实测:迁移一个真实站点

我在一个中型文档站点上做了实际迁移实验。数据如下:

5.1 站点特征

指标数值
Markdown 文件数847
图片数203
自定义 remark 插件2(代码高亮增强 + 自定义 callout)
自定义 rehype 插件1(自定义标题锚点)
Cloudflare 绑定KV + R2 + D1

5.2 迁移步骤

  1. 升级 CLInpx @astrojs/upgrade,无报错
  2. 配置迁移:将 remarkPlugins / rehypePlugins 移到 processor: unified({...})
  3. 评估 Sätteri:执行 npx astro check --processor sätteri,发现两个自定义插件不兼容
  4. 移植自定义插件
    • 代码高亮插件 → Sätteri 原生支持(features: { syntaxHighlight: true }
    • 自定义 callout → 用 Sätteri 的 transforms API 重写(35 行 Rust → JS 绑定)
    • 自定义锚点 → 弃用,改用手动 ID
  5. 启用 cf():在 cloudflare 适配器配置中加 advancedRouting: { cf: true },移除手工绑定代码

5.3 结果

指标迁移前迁移后变化
构建时间87s42s-52%
CI 成本(每月)~$45~$22-51%
Cloudflare 适配器代码47 行3 行-94%
开发-生产 bug 率每月约 2-3 起0(截止评测日)-100%

六、结论:速度 vs 生态

Astro 6.4 问了一个所有 SSG 框架迟早要面对的问题:原生速度值不值得牺牲插件兼容性?

Astro 的回答很务实——不着急,但方向已定。Sätteri 是 opt-in 的,unified 被废弃但还没删。这个过渡窗口给了生态调整的时间。

三个立刻可以带走的东西:

  1. Markdown 管线现在是可插拔的——这意味着将来可能会有 Python 处理器、Go 处理器、浏览器原生处理器
  2. 内容密集项目可以现在就切到 Sätteri,省一半构建时间
  3. Cloudflare 用户几乎没有理由不用 cf()——它把六行绑定压缩成一行,且无副作用

别忘了另一个隐晦信号:Sätteri 这个名字来自瑞典语的”整理/排序”。Astro 团队没有选一个夸张的性能营销词汇,而是选了一个工艺性的词。这不是巧合。


参考

分享本页