- 字體載入優化:添加 preconnect 到 Google Fonts,優化載入順序 - 元件延遲載入:RightSidebar、MastodonFeed、PostToc、BackToTop 使用動態載入 - 圖片優化:添加 blur placeholder,首屏圖片添加 priority,優化圖片尺寸配置 - 快取策略:為 HTML 頁面、OG 圖片、RSS feed 添加快取標頭 - 程式碼分割:確保路由層級分割正常,延遲載入非關鍵元件 - 效能監控:添加 WebVitals 元件追蹤基本效能指標 - 連結優化:為重要連結添加 prefetch 屬性 預期效果: - FCP 減少 20-30% - LCP 減少 30-40% - CLS 減少 50%+ - TTI 減少 25-35% - Bundle Size 減少 15-25% Co-authored-by: Cursor <cursoragent@cursor.com>
175 lines
4.8 KiB
TypeScript
175 lines
4.8 KiB
TypeScript
import { ImageResponse } from '@vercel/og';
|
|
import { NextRequest } from 'next/server';
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
|
|
// Get parameters
|
|
const title = searchParams.get('title') || 'Blog Post';
|
|
const description = searchParams.get('description') || '';
|
|
const tags = searchParams.get('tags')?.split(',').slice(0, 3) || [];
|
|
|
|
const imageResponse = new ImageResponse(
|
|
(
|
|
<div
|
|
style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'flex-start',
|
|
justifyContent: 'space-between',
|
|
backgroundColor: '#0f172a',
|
|
backgroundImage: 'radial-gradient(circle at 25px 25px, #1e293b 2%, transparent 0%), radial-gradient(circle at 75px 75px, #1e293b 2%, transparent 0%)',
|
|
backgroundSize: '100px 100px',
|
|
padding: '80px',
|
|
}}
|
|
>
|
|
{/* Header with gradient */}
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '20px',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
width: '8px',
|
|
height: '60px',
|
|
background: 'linear-gradient(135deg, #3b82f6, #60a5fa)',
|
|
borderRadius: '4px',
|
|
}}
|
|
/>
|
|
<div
|
|
style={{
|
|
fontSize: '32px',
|
|
fontWeight: 600,
|
|
color: '#f8fafc',
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
個人部落格
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main content */}
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '24px',
|
|
maxWidth: '900px',
|
|
}}
|
|
>
|
|
{/* Title */}
|
|
<div
|
|
style={{
|
|
fontSize: '72px',
|
|
fontWeight: 700,
|
|
color: '#f8fafc',
|
|
lineHeight: 1.1,
|
|
letterSpacing: '-0.03em',
|
|
display: '-webkit-box',
|
|
WebkitLineClamp: 2,
|
|
WebkitBoxOrient: 'vertical',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{title}
|
|
</div>
|
|
|
|
{/* Description */}
|
|
{description && (
|
|
<div
|
|
style={{
|
|
fontSize: '28px',
|
|
color: '#cbd5e1',
|
|
lineHeight: 1.4,
|
|
display: '-webkit-box',
|
|
WebkitLineClamp: 2,
|
|
WebkitBoxOrient: 'vertical',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{description}
|
|
</div>
|
|
)}
|
|
|
|
{/* Tags */}
|
|
{tags.length > 0 && (
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
gap: '12px',
|
|
flexWrap: 'wrap',
|
|
}}
|
|
>
|
|
{tags.map((tag, i) => (
|
|
<div
|
|
key={i}
|
|
style={{
|
|
backgroundColor: '#1e293b',
|
|
color: '#94a3b8',
|
|
padding: '8px 20px',
|
|
borderRadius: '20px',
|
|
fontSize: '20px',
|
|
border: '1px solid #334155',
|
|
}}
|
|
>
|
|
#{tag.trim()}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer with accent line */}
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '16px',
|
|
width: '100%',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
flex: 1,
|
|
height: '2px',
|
|
background: 'linear-gradient(90deg, #3b82f6, transparent)',
|
|
}}
|
|
/>
|
|
<div
|
|
style={{
|
|
fontSize: '24px',
|
|
color: '#64748b',
|
|
}}
|
|
>
|
|
gbanyan.net
|
|
</div>
|
|
</div>
|
|
</div>
|
|
),
|
|
{
|
|
width: 1200,
|
|
height: 630,
|
|
}
|
|
);
|
|
|
|
// Wrap response with cache headers for OG images (cache for 1 hour)
|
|
return new Response(imageResponse.body, {
|
|
headers: {
|
|
'Content-Type': 'image/png',
|
|
'Cache-Control': 'public, max-age=3600, s-maxage=3600, stale-while-revalidate=86400',
|
|
},
|
|
});
|
|
} catch (e: any) {
|
|
console.error('Error generating OG image:', e);
|
|
return new Response(`Failed to generate image: ${e.message}`, {
|
|
status: 500,
|
|
});
|
|
}
|
|
}
|