共计 1951 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
在最初使用 Claude API 生成图文卡片的实践中,我们遇到了两个典型问题:

- 性能瓶颈:当用户请求量突增时,直接调用 API 会出现明显延迟。测试显示,在 50QPS 压力下:
- 平均响应时间从 200ms 升至 1200ms
-
因超出速率限制导致的错误率高达 15%
-
样式失控:生成的卡片在不同平台显示效果差异大,主要表现在:
- iOS Safari 上 flex 布局错位
- Android WebView 字体渲染模糊
- 动态插入的 CSS 污染全局样式
技术选型
对比三种主流方案后做出选择:
- SSR 方案:虽然 SEO 友好,但需要维护 Node.js 服务层,增加架构复杂度
- 纯 CSR 方案:首屏性能差,TTFB 时间超过 800ms(实测数据)
- 边缘计算 +CSR:最终选择组合方案:
- 边缘节点缓存 API 响应(Cloudflare Workers)
- 浏览器端 Web Worker 异步渲染
- CSS-in-JS 处理样式隔离
核心实现
缓存策略实现
采用 Redis 双层缓存设计:
// 缓存键设计示例
const generateCacheKey = (params: CardParams) => {
// 关键参数排序后哈希
const {templateId, content, styleType} = params
return `claude:card:${md5(JSON.stringify({
tpl: templateId,
cnt: content.substring(0, 100),
sty: styleType
}).sort())}`
}
// 缓存读取逻辑
async function getCachedResponse(params: CardParams) {const cacheKey = generateCacheKey(params)
const cached = await redis.get(cacheKey)
if (cached) {return JSON.parse(cached) as ClaudeResponse
}
// 未命中时调用 API 并设置缓存
const resp = await claudeApi.generateCard(params)
await redis.setex(cacheKey, 3600, JSON.stringify(resp)) // TTL 1 小时
return resp
}
异步渲染优化
使用 Web Worker 避免 UI 阻塞:
// worker.ts
self.onmessage = async (e) => {const { html, css} = await renderCard(e.data)
postMessage({html, css})
}
// 主线程调用
const worker = new Worker('./card.worker.ts')
export async function renderInWorker(params: RenderParams) {return new Promise<RenderResult>((resolve) => {worker.onmessage = (e) => resolve(e.data)
worker.postMessage(params)
})
}
样式隔离方案
采用 Emotion 实现 CSS-in-JS:
// 卡片容器组件
const CardContainer = styled.div`
${({theme}) => css`
font-family: ${theme.fonts.sans};
@media (prefers-color-scheme: dark) {background: ${theme.colors.gray[800]};
}
`}
`
// 动态注入关键样式
const CriticalCSS = () => (
<Global
styles={css`
.card-img {height: auto; max-width: 100%;}
`}
/>
)
性能测试
优化前后关键指标对比(测试环境:4 核 8G 服务器,100 并发):
| 指标 | 原始方案 | 优化方案 | 提升幅度 |
|---|---|---|---|
| 平均 QPS | 38 | 92 | 142% |
| P99 延迟(ms) | 2100 | 680 | 67%↓ |
| 内存占用(MB) | 450 | 220 | 51%↓ |
避坑指南
- CDN 缓存失效:
- 问题:更新卡片模板后客户端仍显示旧版本
-
解决:在静态资源 URL 添加内容哈希
card.[hash].js -
字体加载闪烁:
- 问题:自定义字体加载期间出现布局偏移(CLS)
-
解决:使用
font-display: optional并预加载关键字体 -
移动端手势冲突:
- 问题:卡片滑动与页面滚动产生冲突
- 解决:添加
touch-action: pan-yCSS 属性
延伸思考
当前方案在超长图文卡片(如商品详情页)的场景下仍存在优化空间:
- 如何实现图片的视口感知懒加载?
- 对于 Markdown 转换的卡片,是否应该预先生成 AST 缓存?
- 在弱网环境下,如何优化渐进式渲染体验?
这些问题的解决方案,或许会成为我们下一个性能突破点。
正文完
发表至: 未分类
近两天内
