Vibe Tutorial
无服务器部署与 CI CD 自动化

11.9 无头模式

Tip

在服务器的暗夜里,有一辆没有显示器的赛车在狂奔。


1. 为什么要学这个?

日常场景: 你想写一个 E2E 测试,验证用户点击登录后是否跳到了首页。 或者你想做一个服务,自动给每个博客文章生成一张截图(Open Graph Image)。 但在 CI 服务器 (GitHub Actions) 或 云函数 (Serverless) 上,没有显示器,没有显卡。 普通的 Chrome 浏览器根本启动不起来。

你需要 Headless Browser (无头浏览器) —— 一个只在内存里渲染,不画在屏幕上的浏览器。


2. 核心概念:Headless

通俗定义

Headless Browser 就像一辆幽灵车

  • 内核: 拥有完整的 Blink/V8 引擎,能解析 JS,能渲染 CSS。
  • 区别: 它不产生像素输出 (No GUI)。它直接在内存里操作 DOM 树。
  • 优势: 速度极快(不需要 GPU 绘图),资源占用少。

常用工具

  • Playwright / Puppeteer: 司機。
  • Headless Chrome: 车。

3. 解决方案 (HOW)

1. 开启 Headless 模式 (Playwright)

Playwright 默认就是无头的。如果你想看它跑,要手动关掉。

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    // true = 无头 (默认,适合 CI)
    // false = 有头 (适合本地调试,看浏览器自己动)
    headless: process.env.CI ? true : false, 
  },
});

2. Serverless 上的挑战 (The 50MB Limit)

如果你想在 Vercel Serverless Function 里跑 Puppeteer (比如用来生成截图),你会遇到大坑。

  • 问题: 完整的 Chromium 浏览器大约 150MB+。
  • 限制: Vercel/AWS Lambda 的包大小限制通常是 50MB。
  • 解法: 使用专门压缩过的 chrome-aws-lambda (现在叫 @sparticuz/chromium)。

运行机制图

graph TD
    Script["测试脚本 (Node.js)"] -->|"指令: page.goto()"| Protocol["DevTools Protocol"]
    
    subgraph Browser ["Headless Chrome (内存中)"]
        Protocol --> JS["V8 引擎 (跑 JS)"]
        Protocol --> Layout["排版引擎 (算位置)"]
        Protocol --> Paint["绘画引擎 (不输出)"]
    end
    
    Paint --"screenshot()"--> Image["截图.png"]
    Layout --"evaluate()"--> Data["提取文字"]
    
    style Browser fill:#eceff1,stroke:#b0bec5
    style Image fill:#fff9c4,stroke:#fbc02d

4. 真实案例

Story

"消失的方块字"

某团队用 Headless Chrome 在 Linux 服务器上自动生成分享海报。 本地 macOS 测试一切正常,生成的图片精美绝伦。 上线后,生成的每一张图片,中文全是 □□□ (豆腐块)。 原因:Linux 服务器 (Ubuntu/Debian) 默认只装了英文字体,没装中文字体。Headless 浏览器渲染时找不到字体,就画了方块。 解法:在 Dockerfile 里安装 fonts-noto-cjk,或者在 Serverless 里加载自定义字体文件。

Vibe 心法: 云端也是一台电脑,但它是一台赤裸的电脑。 不要假设它有字体,不要假设它有屏幕,甚至不要假设它有 GPU。显式声明每一个依赖,是驾驭这台幽灵战车的前提。


5. 本章小结

  1. 无头:CI 环境必须用 Headless 模式。
  2. 调试:本地开发可以用 Headed 模式看过程。
  3. 环境差异:警惕字体缺失和时区问题。