Skip to content

WellChina 速度优化方案

适用范围: 生产域名 wellchina.top(Next.js 15.3 + Vercel Pro + Supabase Tokyo) 配套文档: 速度优化追踪(每次迭代的实测数据)· 全球可访问性与性能监控方案 月费: $20/月(Vercel Pro)

当前状态

Milan 4G iPhone 实测: TTFB 1.54s · LCP 2.43s · CLS 0。LCP 已跨进 Google CWV "Good"(< 2.5s)阈值。详细数据序列见 performance-tracking


架构

┌────────────────────────────────────────────────────────────┐
│   全球用户(NA / EU / 东亚 / SEA / 俄罗斯 等非中国大陆)    │
└──────────────────────────┬─────────────────────────────────┘


┌────────────────────────────────────────────────────────────┐
│   Vercel Edge Network(300+ POPs 全球)                    │
│   • HTML:匿名公共路由 s-maxage=300 + SWR=86400 缓存到 POP │
│   • Static JS/CSS:长缓存 + immutable                       │
│   • Static 图片:/public/(146 张:site-assets + city +    │
│     hospital 图全本地化)                                   │
│   命中率目标 ≥ 95%,TTFB(命中)< 100 ms 全球              │
└──────────────────────────┬─────────────────────────────────┘
                           │ 仅缓存未命中(< 5% 流量)

┌────────────────────────────────────────────────────────────┐
│   Vercel Pro Function(regions: ["hnd1"] Tokyo 单区域)    │
│   • SSR 缓存未命中走这里                                    │
│   • 表单提交 / 写 API / Auth gate / Admin                   │
└──────────────────────────┬─────────────────────────────────┘
                           │ 同区域 ~5 ms RTT

┌────────────────────────────────────────────────────────────┐
│   Supabase Tokyo(ap-northeast-1)— 单一状态源              │
│   • PostgreSQL + Auth + Realtime + Storage                  │
│   • 所有有状态的东西都在这里,零数据复制                    │
└────────────────────────────────────────────────────────────┘

旁路(无区域绑定): DeepL API · Stripe · Resend · Cloudflare Email Routing

为什么是这个架构

WellChina 是读密集应用——99% 流量是匿名浏览(医院/手术/价格/城市/指南),只有联系表单、聊天、注册、admin 是写。这种场景下边缘缓存效率最高,多区域复制是过度工程。

整洁维度体现
单一主区域所有 stateful 服务都在 Tokyo(function / DB / auth / realtime / storage)
零数据复制不用 read replica、不用多 region write、不用同步逻辑
单一 state 供应商Supabase 一家管 DB + Auth + Realtime + Storage
明确两层缓存Vercel CDN(HTML / 静态图)+ Vercel image optimizer —— 没有缓存交叉
函数 region 选择有理论支撑hnd1 跟 Supabase 同 AWS region,function ↔ DB RTT 5 ms
代码层不变不需要"读副本路由"、"region-aware client"等胶水代码

已完成的工作

每项一行说明,详细量测数据见 performance-tracking

改动主要收益Commit
vercel.json: { "regions": ["hnd1"] } — function 跟 Supabase 同 AWS regionTTFB -73%(function ↔ DB RTT 从 ~150ms 降到 ~5ms)2ce6e48
CLS 修复:28 个 @font-facefont-display: swapoptionalCLS 0.186 → 0(完美归零)4128c62
静态图本地化:146 张图(32 site-assets + 13 city + 101 hospital)从 Supabase Storage 搬到 /public/LCP -2.3s(hero/category/hospital 图全部本地化,零 cold-miss)a92a03e
Layout 不再读 cookies/headers:root layout 移除 getLocale()[locale]/layout 移除 auth.getSession() + getRegion()GeoAdvisoryBanner 改 client;首页 priceHighlights 改 client componentSSR 输出对所有访客一致——边缘缓存的前置条件c8d1fdf
首页边缘缓存next.config.ts/ + locale-prefixed `/zhja...Cache-Control: public, s-maxage=300, stale-while-revalidate=86400page.tsxexport const revalidate = 300`
Build 加固:admin / account / sitemap 加 force-dynamic;prisma connection_limit 在 build phase 降到 2 避免打爆 Supabase poolerbuild 稳定562f3b9
/sitemap.xml 24h CDN 缓存sitemap runtime 生成 + CDN 缓存,build 不再查 DB562f3b9

后续可选优化(按 ROI 排序)

1. 扩展边缘缓存到其他公共页

ROI 最高:cache 模板已验证,加 next.config.ts source rule 即可。

目标路径/hospitals/procedures/cities/pricing/compare/guides/search/contact(及它们的 locale 前缀变体)。

前置检查:每页确认无 server-side cookies() / headers() / 用户专属逻辑。如果有(如基于 cookie 的 region price),按首页 priceHighlights 同样套路抽 client component。

预期:这些页 LCP 也跌到 1-2s 区间。

工时:~30 min 配置 + 每页 0~1 h client 化(取决于是否有 region 依赖)。

2. 静态化纯文案页

/about/methodology/privacy/editorial-policy 这 4 页只渲染翻译文本,无 DB / 无 region。

ts
export const dynamic = 'force-static'
export const revalidate = 86400

预期:build-time SSG,永远命中边缘 < 100 ms TTFB。

工时:30 min。

让 hero 在 HTML parsing 阶段开始下载,不等 CSS。

tsx
// app/[locale]/page.tsx 的 <head> 注入
<link rel="preload" as="image" href="/site-assets/brand/hero-bg.webp" />

预期:首页 LCP -200~400 ms。

工时:30 min。

4. WAF Rate Limiting(Pro 含)

/api/contact / /api/search / /api/chat/* 加 rate limiting 规则:

  • /api/contact: 5 请求 / 10 min / IP(防 spam)
  • /api/search: 60 请求 / 1 min / IP(防刷)
  • /api/chat/*: 30 请求 / 1 min / 已认证 user
  • 已知爬虫 + 不在 AI bot 白名单的 UA: deny

预期:节省 function CPU 配额 + 防滥用。

工时:1 h(在 Vercel Dashboard 配置)。

5. Speed Insights RUM 长保留 + 告警

Pro 含 30 天 RUM 保留。配置 Observability 仪表板:按 region 切片 p50/p95 LCP / INP / CLS,设异常告警。

预期:拿到真实用户分布数据,替代 lab 单次 trace 当长期门槛。

工时:30 min。

6. next-intl namespace 拆分

每个 locale 当前是单一 JSON(th.json 84 KB)。拆成 common.json / home.json / hospitals.json / chat.json / ...,每页只下它要的 namespace。

预期:每页 hydration JSON 减 50-70%;首屏 JS payload 减小。

工时:半天~一天(需要审查所有页面用了哪些 keys)。

7. 字体 Critical Subset + Preload

把首屏一定用到的 latin face(Lora 400 + Source Sans 3 400)拆 critical 内联 + preload,其他语种 face 保持异步。

注:当前 font-display: optional 已经避免 CLS。这一步是"让首访用户也能看到 web font"——慢网下首访仍 fallback。如果品牌字体不是核心,可跳。

预期:首访用户 LCP 文本部分 -200~500 ms。

工时:1~2 h。

8. AnimatedSection CSS-only / 减少 framer-motion

useInView + motion.div 给每个 section 引入 React 状态 + 监听器。改用 IntersectionObserver + CSS class,零运行时依赖。

预期:framer-motion 不再进首屏 critical bundle,约 -30~60 KB Gzipped。

工时:2~3 h。

9. ISR revalidateTag for admin writes

Admin 改医院 / 手术 / 价格表后调 revalidateTag('cities') / revalidateTag('procedures'),让缓存按需失效,不等 5 min TTL。

预期:admin 写完立即在公共页生效。

工时:1~2 h。


考虑过但放弃的备选架构

多区域 function(Pro 支持最多 3 个)

regions: ["hnd1", "iad1", "fra1"] 看起来很美好但对我们是反向收益

  • 单 Supabase Tokyo + 多 function region = 非 Tokyo function 每次查询都要跨洋去 Tokyo DB
  • Milan 用户路径:Milan → fra1 function(快)→ Tokyo DB(跨大陆 200ms)→ fra1 → Milan
  • vs 单 hnd1 + edge cache:Milan → hnd1 edge cache 命中 → Milan,不需要 function 执行

要让多区域真正生效需要同时升 Supabase Pro + 加 3 个读副本($25 + ~$30/月,总月费 $75)+ application-level 读写分离代码。ROI 不划算

Edge Runtime + Prisma Accelerate

  • 月费 $20 + $29 = $49
  • 收益:function 在每个 POP 执行(V8 isolate,零冷启动)
  • 代价:Realtime / Auth / Storage 仍走 Tokyo;Node-only API 不可用
  • 边缘缓存命中后已经不执行 function,Edge Runtime 优势不显著

Cloudflare CDN 反代 Supabase Storage

原方案是只搬 site-assets 到 /public/,hospital 图(101 张)继续走 Supabase 但用 CF CDN 反代加速。已被「全 146 张搬 /public/」替代:体验严格更优(零 cold-miss),架构更简单(少一个外部服务)。

Supabase 整体迁 us-east-1

如果不付 $20/月,把 Supabase 迁到 us-east-1 让它跟 Vercel Hobby 锁死的 iad1 function 同 region 也能消除跨洋瓶颈。作为零月费备选保留:迁移涉及 pg_cron / RLS / Storage / Realtime publications 重建,风险高于 hnd1 + Pro。


Vercel Pro 配额参考

以当前流量,月度 usage credit $20 完全覆盖 metered 项目:

Pro 含量超额价格当前用量估计
Fast Data Transfer1 TB$0.15/GB< 50 GB
Edge Requests10 M$2/M< 500 K
CPU Active Time40 hrs$5/hr缓存命中后 ≪ 40h
Image Optimization5 K transformations$0.05/1K第一波 ~10K(地理流量铺开后),后续低
ISR Reads按用$0.40/M每 cache miss 一次
ISR Writes按用$4/M每 revalidate 一次
$20 月度 usage credit$20自动抵扣小型站基本只交基础月费

默认开启 / 自动生效的能力

能力状态备注
Fluid Compute Scale to One自动启用99.37% 请求无冷启动;缓存未命中时 function 永远 warm
Skew Protection自动启用部署期间旧客户端 JS 不会拿到新部署的 chunk URL
Concurrent Builds自动启用
Image Optimization自动启用监控月度 transformation 量,必要时调高 cache TTL
Speed Insights RUM部分启用后续完整启用 + 设置告警(见可选优化 #5)
WAF Rate Limiting未启用后续启用(见可选优化 #4)

UX 已接受的取舍

为了启用边缘缓存(layout 移 client),首屏会有两个轻微 flash:

  • Auth flash:首屏 ~50–200 ms 内 Navbar 显示「未登录」状态,client 端 getSession() 跑完后切到登录态
  • Region flash:首屏 region 默认 US,client 从 cookie 读到正确 region 后切;价格高亮(取决于 region)也会闪一下

回访用户 + 快网络感知不到。Milan 4G 首访用户的 200-400 ms flash 已通过用户测试确认可接受。如果将来产品决定不能容忍,备选方案是 Vercel Edge Config(边缘 KV,< 1ms 读)让 region 仍 server-side 解析。


验收门槛

每次合并优化 PR 后必须在 24h 内重测;如果出现以下情况之一,回滚 PR

  • 任一路径 LCP 比上一基线 +200ms 以上
  • Lighthouse A11y / BP 降 ≥ 3 分
  • WPT Milan 重测 LCP > 8s → 立即排查
  • 任意页面 x-vercel-cache: HIT 比例下降(说明 cache 被破坏)

每完成一个步骤,按同口径重测:WPT Milan iPhone 15 4G + Speed Insights RUM。数据填回 performance-tracking


关键参考资料

WellChina 内部文档 · 基于 VitePress