发布于 2026-01-06 0 阅读
0

如何使用 Satori 和 React 生成动态 OG(OpenGraph)图像

如何使用 Satori 和 React 生成动态 OG(OpenGraph)图像

你有没有注意到,当你通过社交媒体分享链接时,屏幕上会出现一些精美的图片?Twitter、WhatsApp、Discord、Facebook、Slack 等等,都会显示这些图片,以便为你分享的所有链接提供一些背景信息。例如:

链接示例

Woovi 的一大亮点是支付链接。您可以与客户分享此链接,让他们访问所有与消费相关的数据。客户可以通过此链接查看支付 Pix 的二维码、消费金额以及其他可能对他们有用的相关信息。

支付链接示例

分享付款链接

但是,如果我们收到这个支付链接,并将其分享到某个地方会发生什么呢?我们看到的只会是与该图片相关的二维码,如下例所示:

旧支付链接

但是,这真的让人很不爽,您同意吗?它没有提供任何有用的信息,作为客户,我对此很不满意。此外,在某些特定情况下,由于图片尺寸超出预期(例如,WhatsApp 只接受 1200x630 的原始图片),他们甚至无法渲染二维码。

基于这个理念,我们投入大量精力来实现一种全新的运行时生成原始图像的方法。其思路是:根据特定数据动态生成图像,从而获得更优质的图像,让客户更好地理解他们所看到的内容。

使用 Satori 生成图像

在底层,我们将使用Satori作为生成 SVG 的解决方案。我们需要以下这些东西:

  • 用于接收生成图像请求的端点
  • Satori 创建 SVG

很简单,对吧?思路是通过这个接口获取所有必要的数据,然后将其传递给一个 React 组件,该组件将由 Satori 渲染并转换为 SVG。太简单了,对吧?

实现端点

这个接口很简单,和您之前实现的任何其他接口一样。根据我们的技术栈,我们将使用Koa.js来实现它:

import { Router } from '@koa/router';
import type { Context } from 'koa';

const router = new Router();

routerOpenPix.get(
  '/api/link/og/:paymentLinkID{.png}?',
  paymentLinkOgImageGet,
);

function paymentLinkOgImageGet(ctx: Context) {
  // let's implement it in another moment, okay?
}
Enter fullscreen mode Exit fullscreen mode

这个端点的思路很简单:获取参数paymentLinkID,并通过我们物化的数据找到数据库中的所有数据,然后将其传递给 Satori。

重新排列 SVG

既然我们已经实现了端点,接下来就可以按照 Satori 的用法来渲染图像了:

import type { Context } from 'koa';
import satori from 'satori';

async function paymentLinkOgImageGet(ctx: Context) {
  const { paymentLinkID } = ctx.params;

  const paymentLink = await getPaymentLinkByID(paymentLinkID);

  if (!paymentLink) {
    ctx.status = 400;
    ctx.body = 'Error';
    return;
  }

  let svg: string | undefined;

  try {
    const font = await loadFontInArrayBuffer();

    svg = await satori(
      <div style={{ width: 1200, height: 630 }}>
        <h1>{paymentLink.title}</h1>
      </div>,
     {
       fonts: [
        {
          name: 'Font',
          data: font,
          style: 'normal',
          weight: 400,
        },
      ],
    });

    ctx.set('Content-Type', 'image/png');
    ctx.status = 200;
    ctx.body = svg;
  catch (err) {
    ctx.status = 400;
    ctx.body = err;
  }
}
Enter fullscreen mode Exit fullscreen mode

我来向你解释一下我们在这里做什么:

  1. 我们正在查找并验证它是否paymentLink真的存在。
  2. Satori 至少需要加载一个以数组缓冲区形式存储的字体,所以我们在这里加载一个。在这种情况下,您可以根据自己的喜好选择加载字体的方式。例如,从 Google Fonts 加载字体、从本地资源获取字体,或者进行其他任何您喜欢的操作。
  3. 我们正在调用一个satori函数,该函数会根据我们传递的 JSX 为我们渲染 SVG(在底层,他们会将其转换为一个对象)。

关于 Satori 的 CSS,这里有个小细节。Satori 底层使用 Yoga 来渲染页面,所以它无法接受完整的 CSS 规则集,只能接受其中的一部分。你可以在这里看到它支持的所有规则。

如果一切顺利,您可以访问此端点并验证 SVG 是否已正确渲染。

如有必要,您需要将其作为单独的图像返回。为此,我们可以使用以下方法sharp自行处理。请参见以下示例:

  const svg = await satori(); // render the svg
  const img = await sharp(Buffer.from(utf8.decode(svg)))
    .png()
    .toBuffer()
Enter fullscreen mode Exit fullscreen mode

在 HTML 中添加 meta 标签

既然你已经渲染了新的 OG,你需要将其添加到你的 HTML 中,以确保所有爬虫都能获取到它。

为此,您只需在<head>标签中添加两个新标签。请参见下方:

<!DOCTYPE html>
<head>
  <meta property='og:image' content="https://yoururl.com/api/link/og/<dynamic-payment-link-id>.png" />
  <meta property='twitter:image' content="https://yoururl.com/api/link/og/<dynamic-payment-link-id>.png" />
</head>
<!-- ... -->
Enter fullscreen mode Exit fullscreen mode

正如之前帖子中提到的,我们使用服务器端渲染 (SSR) 策略渲染整个支付链接,这样只需获取用户的标识符即可正确插入<dynamic-payment-link-id>。您需要自行验证哪种方法更优。

两者og:image以及twitter: image元标签将确保新的 OG 图片在所有社交媒体和聊天中都能正确显示。

结论

通过这项改进,我们将之前呈现给客户的旧二维码替换为下面这个新二维码:

新支付链接 OG

有了这个新的 OG,我们可以:以更易读的方式获取商家名称、顾客支付的金额、到期日期和二维码。

此外,由于它是动态渲染的,我们可以在运行时改进我们的 OG,以渲染其他状态,例如支付链接已支付、已过期以及不同的场景。

这就是元老级人物通常拥有的强大力量。


欢迎访问Woovi

照片由HassaanUnsplash上拍摄

文章来源:https://dev.to/woovi/how-to-generate-dynamic-og-opengraph-images-with-satori-and-react-1bhb