SEO前端建站

不接生图服务,用 headless Chrome 给站点做 OG 分享图

需要一张 1200×630 的社交分享图。不开设计软件、不接第三方 OG 服务、不上动态生成——用站点自己的 CSS 变量写一个 HTML,headless Chrome 截一张图。以及为什么先做静态默认图、把按篇动态生成留到真需要时。

给站点补 Open Graph 时,卡在一张图上:og:image 需要一张 1200×630 的分享卡,分享到社交平台时显示的就是它。问题不是「做不做得出」,是「用什么方式做,才不给自己添一个长期负担」。

四个选项,三个都嫌重

  • 设计软件手画:Figma 拉一张导出。能做,但它游离在代码之外——改个字、换个配色,得重新打开工具手动来一遍,和站点的设计令牌不同步,迟早漂移。
  • 第三方 OG 服务(各种 og-image.vercel.app 之类):省事,但引入一个外部依赖。个人站最不想要的就是「又一个可能挂、可能改计费、可能被墙的第三方」。
  • 动态按请求生成:Worker 里跑 satori / resvg,把标题实时渲成图。这是正经方案,很多大站在用——但它是一套要部署、要维护的运行时,为一张目前所有页面共享的默认图上这个,是杀鸡用牛刀。
  • 渲染 HTML 再截图:写一个 HTML,用站点自己的样式排好版,让 headless Chrome 截一张 PNG。

我选了第四个。

用站点的设计令牌写一张 HTML

站点是终端风:深色底 #0a0d11、薄荷绿 #3ee6a8、JetBrains Mono 等宽字、细网格背景、顶部一点光晕,logo 是 ~/mahui.me 带一个方块光标。这些全是现成的 CSS 变量。OG 图无非是把同一套令牌铺到 1200×630 的画布上:

<style>
  html, body { width: 1200px; height: 630px; }
  body {
    background: #0a0d11;
    font-family: 'JetBrains Mono', monospace;
    display: flex; align-items: center; justify-content: center;
  }
  /* 细网格 */
  body::before {
    background-image:
      linear-gradient(rgba(62,230,168,.05) 1px, transparent 1px),
      linear-gradient(90deg, rgba(62,230,168,.05) 1px, transparent 1px);
    background-size: 48px 48px;
  }
  .wordmark { font-size: 84px; font-weight: 700; color: #e8eef2; }
  .wordmark .prompt { color: #3ee6a8; }
  .cursor { /* 薄荷绿方块,模仿终端光标 */ }
</style>

关键性质在这:这张图和站点用的是同一套设计令牌,所以它在视觉上永远不会和站点走样。不是「照着站点的样子重画一张」,是「用站点的颜色和字体直接铺一张」——同一个 #3ee6a8、同一个等宽字、同一个 ~/ 前缀。

一条命令截图

Chrome 自带 headless 截图,不需要 Puppeteer、不需要装任何库:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --headless --disable-gpu \
  --screenshot="og-default.png" \
  --window-size=1200,630 \
  --hide-scrollbars \
  "file://$PWD/og.html"

--window-size=1200,630 精确对上 OG 规范尺寸,--hide-scrollbars 防止滚动条占掉边缘几像素。跑完就是一张 1200 x 630, 8-bit RGB 的 PNG,丢进 public/images/og-default.png,head 里引用它。整个过程零依赖、零运行时,产物是一个静态文件。

为什么先做静态默认图

有人会问:为什么不直接上按篇动态生成,把每篇文章的标题烤进图里?那样分享每篇文章都有专属卡片,确实更好看。

答案是 YAGNI。当下的真实需求是「所有页面有一张不丢人的分享图」,一张共享的品牌默认图就满足了。按篇动态生成要解决的是「每篇不同」,而这个价值——文章标题出现在分享卡上——目前排不上优先级:站的分享量还没到让「每篇一张专属图」产生可感收益的程度。为一个还没到来的需求,先背上一套渲染运行时,是典型的提前优化。

但这个方案给未来留了平滑的路:如果哪天真要按篇生成,HTML 模板已经在了。把那段 HTML 加一个标题占位符,构建时对每篇文章循环一次、各截一张图,就从「一张默认图」长成「每篇一张」——同一套设计令牌、同一条截图命令,只是从跑一次变成跑 N 次。静态优先不是放弃动态,是把动态推迟到它值得的那天,且不堵死那条路。

复盘

  • 能用构建期静态产物解决的,别上运行时。一张目前全站共享的图,值不上一套 satori 渲染服务的部署和维护;
  • 让派生资产复用源设计令牌。OG 图用站点同款 CSS 变量渲染,就永远不会和站点视觉漂移——「照着画」会漂,「用同一套变量铺」不会;
  • headless Chrome 是被低估的构建工具。截图、生成 PDF、渲染任何「HTML 能表达但需要变成图片」的东西,一条命令、零依赖,不必动辄上 Puppeteer;
  • 静态优先要留好升级路。先做默认图,但把它写成「一个能加标题占位符的模板」,动态生成就永远只是一个 for 循环的距离,而不是一次重写。

留言

  • 加载中…

留言先审后发,通过后公开显示;邮箱只有站主可见。