别再为渲染吵架了:我把 SSR、SSG、CSR 三件套掰开用,基础设施费直接少了 30%

我有一支技术全面、经验丰富的小型团队,专注高效交付中等规模外包项目,有需要外包项目的可以联系我
流量涨了,账单涨得更快。我们一把梭把所有页面同一种渲染端到端扫过去,然后就为这个选择分分秒秒付钱。
直到我们老老实实把每个页面随时间与操作的变化轨迹画出来——渲染策略按变化模式拆分。
结果?仪表盘安静了,发票瘦了。故事的精华,就在“拆”的过程里。
三种渲染,配一张“地图”,争论瞬间闭麦
我们的用户很多在中端机 + 慢网上;新鲜数据只在少数页面必须现做,大多数页面几乎不变。
所以我们画了张行为 → 渲染的对应图。它比任何文档都更有说服力——成本的曲线和箭头对上了。
Request
|
+——+——+
| |
SSR CDN
| |
Fresh Cached
| |
HTML SSG
| |
Hydrate CSR islands
该不再调用的端点不再“机械劳动”;CDN开始扛大头,应用只给真正动态的路径出力。
这张小图就贴在任务墙上,每条路由的修改都对着它走。
只把“必须新鲜”的,留给 SSR
我们把服务端渲染严格限在:依赖当前访问者、鉴权态、或按请求过滤的页面。其它统统撤席。 服务器只返回可直接展示的 HTML,再配一份瘦身后的 hydrate 载荷。
// pages/orders/[id].js
export async function getServerSideProps({ params, req }) {
const user = await getUser(req);
const order = await api.orders.get(params.id, { user });
return { props: { user: pick(user, [“id”]), order } }; // small props
}

export default function OrderPage({ user, order }) {
return ;
}
冷启动 TTFB 变快,因为链路短且可预期;热请求在合规前提下用短期、精细的私有键吃缓存。 想复制?先审一遍 SSR 路由:如果 90% 的访客看到的 HTML 都一样,它大概率不该 SSR。因此,做减法。

大多数页面交给 SSG + 定时再验证(ISR)
营销、文档、FAQ、列表页变化慢。它们改成静态生成 + 再验证。构建期做重活,线上交给 CDN 直接飞:
case | p95 TTFB | monthly cost
————–+———-+————-
SSG reval 60s | 45 ms | –27%
SSR baseline | 220 ms | 0%
命中率上来了,源站 CPU下去了,因为我们不再重复渲染。编辑用定时 revalidate,不发版也能更新。 给你的建议:从一个高流量、低波动的路径下手;先用 几十秒的再验证,预览流程稳了再拉长。
需要“活在加载之后”的交互,用 CSR 小岛
筛选器、图表、编辑器这类纯交互,我们做成 CSR islands:壳子快、组件活。
// pages/products/index.js
export async function getStaticProps() {
const featured = await api.products.featured();
return { props: { featured }, revalidate: 120 };
}

// components/Filters.client.jsx
export default function Filters({ onChange }) {
const [state, set] = useState(init());
useEffect(() => onChange(state), [state, onChange]);
return ;
}
首屏来自静态 HTML,交互再按岛屿渐进水合。用户滚、点、打字不必等网络。 加岛时记住:props 小且稳定。为了改一个 slider 而整页重渲染,卡顿和云费用都会跟你急。
把“缓存键”和“路由”对齐,才算真正拆完
方案一开始并不灵:缓存键没贴着页面的变化点走。 我们把数据流摊开,标注键该放哪、不该放哪:
Client -> CDN(key: /docs/v3/page)
|
HIT -> HTML
MISS-> Origin
|
SSG build or revalidate
去掉吵闹的 query后,命中率上去、源站继续打哈欠。 鉴权页绕开 CDN;文档页新鲜时根本不碰应用。 如果你的缓存像随机数,在预发里把缓存键打印到响应头,再压测扫一遍——问题会自己跳出来。
写进路由与构建规则,团队不用靠记忆
我们把拆分规则代码化,让“选模式”跟着路径走,而不是某次迭代会上自由心证。

// next.config.js (idea sketch)
const SSR = [/^\/account/, /^\/orders\/\w+$/];
const SSG = [/^\/docs/, /^\/pricing/, /^\/catalog/];

module.exports = {
async headers() {
return [
{ source: “/docs/:slug“, headers: [{ key: “Cache-Control”, value: “s-maxage=60, stale-while-revalidate=300” }] }, { source: “/account/:path“, headers: [{ key: “Cache-Control”, value: “no-store” }] },
];
},
async rewrites() {
return [
{ source: “/catalog”, destination: “/_static/catalog” },
{ source: “/orders/:id”, destination: “/_ssr/orders/:id” },
];
},
};
包更小、SSR 机器约少三成、静态页 p95 TTFB腰斩。最大的红利是:流量尖峰来了也稳。把规则贴近路由,这样偏移会出现在代码评审,而不是云账单。
上线前的“必修四句真话”
别迷信一种模式。把渲染策略和数据的时间变化对齐,才是降本增稳的正解。
SSR 留给真正个性化的视图;SSG+短 revalidate 扛大盘;CSR 小岛负责灵活交互。
这套做法能规模化,是因为它把“口水战”变成路由、键与代码。
账单不值就别硬顶——挑一条忙路径,先拆它。
如果你有更瘦的招,请丢在下面——让我们心安理得地“互抄作业”。

全栈AI·探索:涵盖动效、React Hooks、Vue 技巧、LLM 应用、Python 脚本等专栏,案例驱动实战学习,点击二维码了解更多详情。

声明:来自JavaScript 每日一练,仅代表创作者观点。链接:https://eyangzhen.com/3785.html

JavaScript 每日一练的头像JavaScript 每日一练

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部