想讓你的部落格或產品頁在社群平台上更吸睛?這篇文章教你如何用 Vercel 開源工具 Satori,快速生成動態 OG 圖像,支援 Next.js、Astro、Hono 等框架,提升分享效率與視覺質感。
在社群平台上分享連結時,你是否也常被預覽圖的設計卡住? 預設封面太普通、自製圖片太麻煩、還要配合文章動態變化……這些問題,我以前也都遇過。
直到我發現了 Vercel 推出的開源工具 Satori —— 一個能用 React 語法(JSX)直接產出 SVG 的圖像引擎,支援在 Next.js、Astro、Hono 等框架中動態生成高質感 Open Graph 圖像。不只效能快、成本低,還能自訂字體、主題、甚至配合 Markdown 自動化產圖。
本篇文章會從原理、實作到最佳化策略,手把手帶你用程式碼打造屬於自己風格的 OG 圖像預覽系統。讓你從此不再為「分享出去長什麼樣」而苦惱,也讓社群連結變得更吸睛!
什麼是 Satori
Satori 是 Vercel 創建的一個開源庫,專門用於將 HTML 和 CSS 轉換為 SVG 圖像。它的名稱來源於日語「悟り」,意為啟發
或理解
。Satori 的主要用途是生成動態的 Open Graph 圖像,這些圖像可用於社交媒體分享時顯示的預覽卡片。
Satori 支持 JSX 語法,這使得定義圖像內容變得非常直觀。它處理佈局計算、字體渲染、排版等工作,生成的 SVG 與瀏覽器中的 HTML 和 CSS 呈現效果完全匹配。基本用法示例:
import satori from 'satori';
const svg = await satori(
<div style={{ color: 'black' }}>
hello, world
</div>,
{
width: 600,
height: 400,
fonts: [
{
name: 'Roboto',
data: robotoArrayBuffer,
weight: 400,
style: 'normal',
},
],
}
);
這段代碼會生成一個包含黑色文字”hello, world”的 SVG,尺寸為 600×400。
誰在使用 Satori
Satori 的應用範圍廣泛且多樣化。Vercel 作為開發者將其整合到 @vercel/og
庫中,為 Next.js 應用提供動態 OG 圖像生成功能。個人部落格開發者利用 Satori 自動為文章生成 OG 圖像,展示標題、作者和閱讀時間等元數據。Cloudflare Pages 則提供了 Vercel OG 插件,讓開發者能在其平台上輕鬆創建動態 OG 圖像。此外,越來越多的企業正採用 Satori 為產品頁面、文章和活動生成引人注目的社交媒體預覽圖像,充分展現了 Satori 在各種網絡應用場景中的實用性和靈活性。
為什麼選擇 Satori
Satori 相比傳統的 OG 圖像生成方法有幾個明顯的技術優勢:
-
高性能:Vercel 聲稱,與傳統的基於無頭瀏覽器(如 Chromium)的方法相比,Satori 的生成速度快 5 倍。它不需要啟動整個瀏覽器實例,而是直接將 HTML/CSS 轉換為 SVG。
-
Edge 兼容:Satori 可以在 Edge 運行時環境中運行,如 Vercel Edge Functions 和 Cloudflare Workers,這使得圖像生成可以更接近用戶,減少延遲。
-
成本效益:在 Edge Functions 中運行 Satori 比在無服務器函數中運行 Chromium 便宜約 160 倍。
-
不依賴 DOM:Satori 不依賴於瀏覽器的 DOM,這意味著它可以在服務器端或其他沒有瀏覽器環境的地方運行。
-
自動緩存:
@vercel/og
會自動添加適當的標頭,以在 Edge 上緩存計算生成的圖像,幫助減少成本和重複計算。
在不同環境中的整合示例
在 Astro 中使用 Satori
在 Astro 中,你可以使用 satori-html
包來處理 HTML 模板字符串:
import type { APIContext } from "astro";
import { html } from "satori-html";
import satori from "satori";
import { readFileSync } from "fs";
export async function GET({ params }: APIContext) {
const fontData = readFileSync("./public/fonts/Roboto-Regular.ttf");
const markup = html(`
<div
style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: white; font-family: 'Roboto';"
>
<div style="display: flex; font-size: 60px; font-weight: 700;">
Hello, Astro!
</div>
</div>
`);
const svg = await satori(markup, {
width: 1200,
height: 630,
fonts: [
{
name: "Roboto",
data: fontData,
weight: 400,
style: "normal",
},
],
});
return new Response(svg, {
headers: {
"Content-Type": "image/svg+xml",
"Cache-Control": "public, max-age=31536000, immutable",
},
});
}
這個方法允許在 Astro 的伺服器端點中生成動態 OG 圖像。
在 Hono.js 與 Next.js 整合中使用
對於使用 Hono.js 和 Next.js 的項目,你可以在 API 路由中集成 Satori:
// app/api/[...route]/route.tsx
import { Hono } from 'hono';
import { handle } from 'hono/vercel';
import satori from 'satori';
import { readFileSync } from 'fs';
const app = new Hono();
app.get('/api/og', async (c) => {
const fontData = readFileSync('./public/fonts/Roboto-Regular.ttf');
const svg = await satori(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 48,
background: '#f6f6f6',
color: '#333',
fontFamily: 'Roboto',
}}>
Generated with Hono and Satori
</div>,
{
width: 1200,
height: 630,
fonts: [
{
name: 'Roboto',
data: fontData,
weight: 400,
style: 'normal',
},
],
}
);
return c.body(svg, {
headers: {
'Content-Type': 'image/svg+xml',
'Cache-Control': 'public, max-age=31536000, immutable',
}
});
});
export const GET = handle(app);
export const POST = handle(app);
這個示例展示了如何在 Hono.js 和 Next.js 集成的環境中使用 Satori。
注意事項和調試技巧
在使用 Satori 時,有一些常見的陷阱和限制需要注意:
JSX 兼容性問題
在某些環境中,直接使用 JSX 可能會遇到問題。解決方案包括:
- 使用
satori-html
來處理 HTML 模板字符串(如 Astro 示例中所示) - 使用非 JSX 語法:如果沒有啟用 JSX 轉譯器,你可以直接傳遞類似 React 元素的對象:
await satori(
{
type: 'div',
props: {
children: 'hello, world',
style: { color: 'black' },
},
},
options
)
這個問題可以通過使用 React 的 createElement
方法來解決。React.createElement 是 React 庫的核心方法之一,它允許開發者以編程方式創建 React 元素,而不需要使用 JSX 語法。這種方法特別有用於那些不支持或不想使用 JSX 的環境中。通過直接調用 createElement,你可以構建出與 JSX 等效的元素結構,從而繞過 JSX 轉換的步驟,同時保持代碼的可讀性和功能性。這種技術不僅解決了特定環境下的兼容性問題,還為開發者提供了更底層的 React 元素操作方式。
字體渲染問題
字體渲染是 Satori 中最常見的問題之一:
-
缺少字體:Satori 默認只包含 Noto Sans 字體。對於其他字體,你需要手動加載字體文件。
-
缺少字形:某些字符(特別是中文、日文等非拉丁字符)可能會因為字體缺少相應的字形而不顯示。
// 支持中文字體的示例
const notoSansSCData = await fs.promises.readFile('./NotoSansSC-Regular.ttf');
const svg = await satori(
你好,世界!,
{
width: 600,
height: 400,
fonts: [
{
name: 'Noto Sans SC',
data: notoSansSCData,
weight: 400,
style: 'normal',
},
],
}
);
某些字符在中文和日文中有不同的字形,但它們的 Unicode 是相同的。如果你需要同時顯示中文和日文字符,可能需要使用專門的字體。
調試消失的元素
如果元素不顯示或”消失”,可能是由於以下原因:
-
CSS 限制:Satori 只支持 CSS 的一個有限子集。
-
字體問題:如前所述,確保加載了正確的字體和字形。
-
命名或版本控制:有時,在複製和重命名元素後,它們可能會”消失”。這可能是由於撤銷操作或隱藏的命名衝突。
啟用調試模式可以幫助解決這些問題:
const svg = await satori(
Your content here,
{
width: 600,
height: 400,
fonts: [...],
debug: true, // 啟用調試模式
}
);
高級使用想法
主題切換(明/暗模式)
為不同的主題提供不同版本的 OG 圖像:
const generateOGImage = async (theme = 'light') => {
const isDark = theme === 'dark';
const svg = await satori(
<div style={{
background: isDark ? '#1a1a1a' : '#ffffff',
color: isDark ? '#ffffff' : '#1a1a1a',
// ... 其他樣式
}}>
<h1>我的文章標題</h1>
<p>作者:Jane Doe</p>
</div>,
{
width: 1200,
height: 630,
fonts: [...],
}
);
return svg;
};
// 使用
app.get('/api/og', async (c) => {
const theme = c.req.query('theme') || 'light';
const svg = await generateOGImage(theme);
// ... 返回 SVG
});
從 Markdown frontmatter 自動生成 OG 圖像
在像 Next.js 或 Astro 這樣的框架中,你可以從 Markdown 文件的 frontmatter 數據自動生成 OG 圖像:
// 假設你有一個處理 Markdown 的函數
import { getPostBySlug } from '../lib/posts';
export default async function handler(req, res) {
const { slug } = req.query;
const post = getPostBySlug(slug);
const svg = await satori(
<div style={{
// ... 樣式
}}>
<h1>{post.title}</h1>
<p>作者:{post.author}</p>
<p>發布於:{new Date(post.date).toLocaleDateString()}</p>
{post.coverImage && (
<img
src={`data:image/jpeg;base64,${post.coverImageBase64}`}
style={{ width: 300, height: 200, objectFit: 'cover' }}
/>
)}
</div>,
{
width: 1200,
height: 630,
fonts: [...],
}
);
// 返回 SVG
res.setHeader('Content-Type', 'image/svg+xml');
res.send(svg);
}
CDN 緩存策略
為了提高性能和減少生成成本,實施有效的 CDN 緩存策略至關重要:
export default async function handler(req, res) {
// ... 生成 SVG
// 設置緩存標頭
res.setHeader('Cache-Control', 'public, max-age=86400, s-maxage=604800, stale-while-revalidate=31536000');
res.setHeader('Content-Type', 'image/svg+xml');
res.send(svg);
}
Vercel 的 @vercel/og
會自動添加適當的標頭,在 Edge 上緩存計算生成的圖像,幫助減少成本和重複計算。
結論
Satori 提供了一個高效、靈活的解決方案,用於在現代前端框架中生成動態 OG 圖像。與傳統的基於瀏覽器的方法相比,它擁有顯著的性能和成本優勢。無論你使用的是 Next.js、Astro、Cloudflare Pages 還是 Hono.js,Satori 都能幫助你創建高質量的社交媒體預覽圖像。
隨著社交媒體分享在數字營銷中的重要性不斷增長,像 Satori 這樣的工具將變得越來越重要。通過本文介紹的技術和示例,你應該能夠在自己的專案中輕鬆集成 Satori,並開始創建引人注目的動態 OG 圖像。