Astro 4.8 博客系统完整实现 - 包含 AI 翻译和 E2E 测试的全面方法
基于 Astro Content Collections 实现高性能博客系统的开发记录。详细介绍使用 Google Gemini API 进行 AI 自动翻译、Playwright E2E 测试以及速率限制功能的全面开发过程。
概述
最近的开发工作中,我们基于 Astro 4.8.7 实现了一个功能全面的博客系统。这个系统不仅仅是简单的 Markdown 显示,而是一个现代化的技术博客平台,集成了 AI 自动翻译、端到端(E2E)测试以及为实际运营而设计的各项功能。
告别了 WordPress 中与小部件(Widgets)的纠缠。现在,你可以让 AI 来翻译,自动化测试,然后让工程师们安心睡觉(开玩笑的)。
技术栈
核心技术
- Astro 4.8.7: 基于 Islands Architecture 的高速静态站点生成器 (SSG)
- TypeScript: 类型安全的开发环境
- Tailwind CSS: 采用 Utility-first 方式进行样式设计
- Alpine.js 3.14.0: 轻量级前端 JavaScript 框架
博客专属功能
- Content Collections: Astro 中提供类型安全的 Content 管理功能
- Google Gemini API: AI 自动翻译引擎
- Playwright: 端到端 (E2E) 测试自动化工具
- remark-link-card-plus: 生成丰富的链接卡片
核心架构实现
1. Content Collections 设计
我们从传统的 Markdown 文件管理转向了 Astro 的 Content Collections
。这样就再也不用担心“咦?这个 frontmatter 的写法是不是对的?”了:
// src/content/config.ts
const blogCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
category: z.enum(['ai', 'engineering', 'founder', 'lab']),
tags: z.array(z.string()).default([]),
lang: z.enum(['ja', 'en']).default('ja'),
featured: z.boolean().default(false),
}),
});
优点:
- 构建时进行类型验证,防止元数据错误
- 热重载 (Hot reloading),实现实时更新
- 自动化的 Schema 验证
2. AI 翻译系统设计
我们利用 Google Gemini API 构建了自动翻译系统。过去需要数天才能完成的人工翻译,现在 AI 可以在几秒钟内搞定,而且还不抱怨:
// scripts/translate-blog.js
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash-lite-preview-06-17"
});
async function translateBlogPost(japaneseContent) {
const prompt = `Translate the following Japanese blog post to English.
Translate technical terms appropriately and make it readable English:
${japaneseContent}`;
const result = await model.generateContent(prompt);
return result.response.text().trim();
}
特点:
- 速率限制: 保守地设置为每分钟 15 个请求
- 错误处理: 采用指数退避 (Exponential Backoff) 策略进行重试
- YAML 安全性: 对 frontmatter 进行恰当的转义处理
3. 速率限制系统
为了 API 的稳定运行,我们实现了速率限制功能。这样就不会被 Google 警告“你请求太多了!”:
// scripts/rate-limiter.js
class RateLimiter {
constructor({ requestsPerMinute = 15, maxRetries = 3 }) {
this.requestsPerMinute = requestsPerMinute;
this.maxRetries = maxRetries;
this.requestTimes = [];
}
async executeWithRetry(operation, description) {
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
await this.checkRateLimit();
return await operation();
} catch (error) {
if (attempt === this.maxRetries) throw error;
await this.backoff(attempt);
}
}
}
}
4. E2E 测试实现
我们使用 Playwright 实现了对博客功能的全面测试。不能再简单地认为“能跑就行”了:
// e2e/blog.spec.ts
test.describe('Blog functionality', () => {
test('should navigate to Japanese blog', async ({ page }) => {
await page.goto('/blog/');
await expect(page.locator('h1')).toContainText('技術ブログ');
});
test('should display OGP image for blog posts', async ({ page }) => {
await page.goto('/blog/');
const firstPost = page.locator('[data-testid="blog-post"]').first();
await firstPost.click();
const ogImage = page.locator('meta[property="og:image"]');
await expect(ogImage).toHaveAttribute('content', /\/og\/.*\.svg/);
});
});
性能优化
压缩与优化设置
// astro.config.mjs
integrations: [
compress({
CSS: true,
HTML: {
'remove-comments': true,
'minify-js': true,
'minify-css': true
},
JavaScript: true,
SVG: true
}),
compressor({
gzip: true,
brotli: true
})
]
图片优化
- 以 AVIF 格式提供图片
- 提供 WebP 作为回退选项
- 响应式图片尺寸(480w, 800w)
挑战与解决方案
1. YAML Frontmatter 转义问题
挑战: 当翻译后的标题或描述包含特殊字符时,会导致 YAML 解析错误。
解决方案:
function escapeYAMLString(str) {
return str
.replace(/\\/g, "\\\\") // 先转义反斜杠
.replace(/"/g, '\\"'); // 再转义双引号
}
2. API 速率限制应对
挑战: Gemini API 的限制导致请求失败。
解决方案: 指数退避与智能速率限制
- 基于秒的延迟
- 最大延迟扩展至 60 秒
- 通过追踪请求时间进行预先限制
3. E2E 测试的稳定性
挑战: 动态内容导致测试不稳定。
解决方案: 使用 data-testid
属性进行可靠的元素定位。
运营流程
新文章发布流程
- 创建日语文章: 在
/src/content/blog/ja/
目录下创建 Markdown 文件。 - AI 翻译: 运行
npm run translate src/content/blog/ja/article-name.md
。 - 执行测试: 运行
npm run test:e2e
。 - 构建: 运行
npm run build
。 - 部署: 自动部署到 Firebase Hosting。
质量保证
- 类型检查: 通过 TypeScript 在编译时进行检查。
- Schema 验证: 使用 Zod 进行内容验证。
- E2E 测试: 使用 Playwright 进行功能测试。
学习心得
1. Content Collections 的强大之处
与传统的基于文件管理相比,类型安全性和开发体验得到了极大的提升。特别是,当 Schema 发生变更时,影响范围会变得非常清晰,使得重构更加安全。
现在 TypeScript 会“报错”告诉你“类型不匹配!”,这样就再也不会因为运行时错误而惊慌失措了。太棒了。
2. AI 翻译的实用性
Google Gemini API 在技术文章翻译方面的质量非常高,大大减少了人工微调的工作量。但专业术语的一致性方面仍需持续改进。
当我看到“npm”被翻译成“エヌピーエム”(NPM 的日语音译)时,我笑了。虽然也不能说错,但确实有点意思。
3. E2E 测试的重要性
即使是静态站点生成器,对于路由、元数据生成等复杂功能,E2E 测试也必不可少。
真想给自己两巴掌,过去居然认为“静态网站不需要测试”。
性能结果
- 首次内容绘制 (First Contentful Paint): 0.8 秒
- 最大内容绘制 (Largest Contentful Paint): 1.2 秒
- 累积布局偏移 (Cumulative Layout Shift): 0.05
- 总包体大小: 45KB (gzip 压缩后)
总结
通过这次博客系统实现,我们再次确认了现代 JAMstack 架构在以下几方面的关键作用:
- 类型安全: 通过 Content Collections 在设计阶段就防止错误(再也不会因为运行时错误而哭泣)。
- 自动化: AI 翻译和 E2E 测试提高了运营效率(正在逐渐取代人类的工作)。
- 性能: 通过积极的优化实现极速加载(速度之巅)。
- 可维护性: 清晰的架构带来了可持续的开发(对未来的自己更友好)。
作为技术博客平台,我们实现了从文章撰写到发布的全流程自动化,为开发者创造了一个可以专注于核心内容创作的环境。
换句话说,这意味着我再也没有借口偷懒不更新博客了。这…太美好了吗?
Support this article
If this article was helpful, please support us with a tip. You can choose any amount.
相关文章

【全五语种支持】从仅日英到支持中韩西的3小时激战记
原以为支持五语种,实则只有两种的震撼事实。与 Claude Code 一起,飞速实现中文、韩文、西班牙语支持的全记录。

整个 Google Cloud 项目一天之内完成组织间迁移,我的经验总结
大家好,我是 Terisuke。

挑战阿部宽的官网后,我构建了一个超棒的网站
我自诩为“拥有最快下载速度的网站的男人”,一直以来都在挑战阿部宽的官网。结果,我构建了一个超越单纯速度竞争的、真正高性能的网站。顺带一提,阿部宽的网站至今仍然很快。致以敬意。