[{"data":1,"prerenderedAt":4194},["ShallowReactive",2],{"article-/topics/next/nextjs-fullstack-saas-guide":3,"related-next":615,"content-query-cek3rd11j3":3734},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":12,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":23,"_type":609,"_id":610,"_source":611,"_file":612,"_stem":613,"_extension":614},"/topics/next/nextjs-fullstack-saas-guide","next",false,"","构建全栈 SaaS 应用：Next.js 从认证到计费的产品化落地指南","面向真实 SaaS 场景，系统讲清 Next.js 全栈应用的目录结构、认证、数据库、订阅计费、后台管理与部署观测，避免把演示项目误当产品架构。","2026-04-06","HTMLPAGE 团队",[13,14,15,16,17],"Next.js","SaaS","全栈开发","订阅计费","认证系统","/images/topics/next/nextjs-fullstack-saas-guide.jpg","saas dashboard product team laptop",20044382,"https://www.pexels.com/photo/hands-of-men-working-on-laptop-20044382/",16,{"type":24,"children":25,"toc":591},"root",[26,35,41,66,71,77,82,187,192,198,203,216,221,227,232,237,256,261,267,272,295,300,305,323,329,334,357,362,368,373,391,396,419,425,506,512,519,524,530,535,541,546,551],{"type":27,"tag":28,"props":29,"children":31},"element","h2",{"id":30},"构建全栈-saas-应用",[32],{"type":33,"value":34},"text","构建全栈 SaaS 应用",{"type":27,"tag":36,"props":37,"children":38},"p",{},[39],{"type":33,"value":40},"很多 Next.js 教程能带你做完一个“能运行的产品演示”，但离真正的 SaaS 还差一整层工程现实：",{"type":27,"tag":42,"props":43,"children":44},"ul",{},[45,51,56,61],{"type":27,"tag":46,"props":47,"children":48},"li",{},[49],{"type":33,"value":50},"账号体系是否可扩展。",{"type":27,"tag":46,"props":52,"children":53},{},[54],{"type":33,"value":55},"订阅和权限是否能对齐。",{"type":27,"tag":46,"props":57,"children":58},{},[59],{"type":33,"value":60},"后台和用户端是否能共享同一套模型。",{"type":27,"tag":46,"props":62,"children":63},{},[64],{"type":33,"value":65},"上线后出了问题怎么追。",{"type":27,"tag":36,"props":67,"children":68},{},[69],{"type":33,"value":70},"SaaS 的难点不是把页面做出来，而是把“产品规则”落进系统边界。",{"type":27,"tag":28,"props":72,"children":74},{"id":73},"_1-先把-saas-的核心对象定义清楚再写页面",[75],{"type":33,"value":76},"1. 先把 SaaS 的核心对象定义清楚，再写页面",{"type":27,"tag":36,"props":78,"children":79},{},[80],{"type":33,"value":81},"一个最小 SaaS 通常至少有这几层对象：",{"type":27,"tag":83,"props":84,"children":85},"table",{},[86,110],{"type":27,"tag":87,"props":88,"children":89},"thead",{},[90],{"type":27,"tag":91,"props":92,"children":93},"tr",{},[94,100,105],{"type":27,"tag":95,"props":96,"children":97},"th",{},[98],{"type":33,"value":99},"对象",{"type":27,"tag":95,"props":101,"children":102},{},[103],{"type":33,"value":104},"作用",{"type":27,"tag":95,"props":106,"children":107},{},[108],{"type":33,"value":109},"常见错误",{"type":27,"tag":111,"props":112,"children":113},"tbody",{},[114,133,151,169],{"type":27,"tag":91,"props":115,"children":116},{},[117,123,128],{"type":27,"tag":118,"props":119,"children":120},"td",{},[121],{"type":33,"value":122},"User",{"type":27,"tag":118,"props":124,"children":125},{},[126],{"type":33,"value":127},"登录主体",{"type":27,"tag":118,"props":129,"children":130},{},[131],{"type":33,"value":132},"直接把权限全挂到 user 上",{"type":27,"tag":91,"props":134,"children":135},{},[136,141,146],{"type":27,"tag":118,"props":137,"children":138},{},[139],{"type":33,"value":140},"Workspace / Tenant",{"type":27,"tag":118,"props":142,"children":143},{},[144],{"type":33,"value":145},"团队或组织边界",{"type":27,"tag":118,"props":147,"children":148},{},[149],{"type":33,"value":150},"单用户模型难扩团队",{"type":27,"tag":91,"props":152,"children":153},{},[154,159,164],{"type":27,"tag":118,"props":155,"children":156},{},[157],{"type":33,"value":158},"Subscription",{"type":27,"tag":118,"props":160,"children":161},{},[162],{"type":33,"value":163},"计费套餐",{"type":27,"tag":118,"props":165,"children":166},{},[167],{"type":33,"value":168},"只做页面展示，不落权限",{"type":27,"tag":91,"props":170,"children":171},{},[172,177,182],{"type":27,"tag":118,"props":173,"children":174},{},[175],{"type":33,"value":176},"Resource",{"type":27,"tag":118,"props":178,"children":179},{},[180],{"type":33,"value":181},"真正的业务数据",{"type":27,"tag":118,"props":183,"children":184},{},[185],{"type":33,"value":186},"没有 tenant 隔离",{"type":27,"tag":36,"props":188,"children":189},{},[190],{"type":33,"value":191},"如果你一开始只设计 User，而没有 Workspace 或订阅对象，后面做团队版和计费升级会很痛苦。",{"type":27,"tag":28,"props":193,"children":195},{"id":194},"_2-目录结构要围绕领域和权限边界组织",[196],{"type":33,"value":197},"2. 目录结构要围绕“领域”和“权限边界”组织",{"type":27,"tag":36,"props":199,"children":200},{},[201],{"type":33,"value":202},"对 SaaS 来说，比起纯组件分层，更重要的是业务边界清晰：",{"type":27,"tag":204,"props":205,"children":210},"pre",{"className":206,"code":208,"language":209,"meta":7},[207],"language-txt","app/\n  (marketing)/\n  (auth)/\n  dashboard/\n  api/\nlib/\n  auth/\n  billing/\n  db/\n  permissions/\n","txt",[211],{"type":27,"tag":212,"props":213,"children":214},"code",{"__ignoreMap":7},[215],{"type":33,"value":208},{"type":27,"tag":36,"props":217,"children":218},{},[219],{"type":33,"value":220},"这样做的好处是：认证、计费、权限和数据访问的边界更清晰，不会让每个页面各写一套判断逻辑。",{"type":27,"tag":28,"props":222,"children":224},{"id":223},"_3-认证只是入口权限才决定产品能不能扩展",[225],{"type":33,"value":226},"3. 认证只是入口，权限才决定产品能不能扩展",{"type":27,"tag":36,"props":228,"children":229},{},[230],{"type":33,"value":231},"很多项目做完登录就觉得认证系统完成了，结果一到团队版就开始崩。",{"type":27,"tag":36,"props":233,"children":234},{},[235],{"type":33,"value":236},"更稳的思路是：",{"type":27,"tag":238,"props":239,"children":240},"ol",{},[241,246,251],{"type":27,"tag":46,"props":242,"children":243},{},[244],{"type":33,"value":245},"认证解决“你是谁”。",{"type":27,"tag":46,"props":247,"children":248},{},[249],{"type":33,"value":250},"授权解决“你能做什么”。",{"type":27,"tag":46,"props":252,"children":253},{},[254],{"type":33,"value":255},"计费解决“你在什么套餐下能做什么”。",{"type":27,"tag":36,"props":257,"children":258},{},[259],{"type":33,"value":260},"这三件事如果不拆开，后面每加一个套餐或角色，复杂度都会乘法上涨。",{"type":27,"tag":28,"props":262,"children":264},{"id":263},"_4-一个常见失败案例先写计费页面后补订阅状态结果权限全靠-if-判断",[265],{"type":33,"value":266},"4. 一个常见失败案例：先写计费页面，后补订阅状态，结果权限全靠 if 判断",{"type":27,"tag":36,"props":268,"children":269},{},[270],{"type":33,"value":271},"事故链路通常是：",{"type":27,"tag":238,"props":273,"children":274},{},[275,280,285,290],{"type":27,"tag":46,"props":276,"children":277},{},[278],{"type":33,"value":279},"先接入支付。",{"type":27,"tag":46,"props":281,"children":282},{},[283],{"type":33,"value":284},"前端根据套餐名隐藏按钮。",{"type":27,"tag":46,"props":286,"children":287},{},[288],{"type":33,"value":289},"后端没有真正校验权限。",{"type":27,"tag":46,"props":291,"children":292},{},[293],{"type":33,"value":294},"用户只要知道接口就能越权调用。",{"type":27,"tag":36,"props":296,"children":297},{},[298],{"type":33,"value":299},"根因是把“展示层限制”误当成“权限系统”。",{"type":27,"tag":36,"props":301,"children":302},{},[303],{"type":33,"value":304},"修复方式应该是：",{"type":27,"tag":238,"props":306,"children":307},{},[308,313,318],{"type":27,"tag":46,"props":309,"children":310},{},[311],{"type":33,"value":312},"订阅状态落库。",{"type":27,"tag":46,"props":314,"children":315},{},[316],{"type":33,"value":317},"权限判断放到服务端。",{"type":27,"tag":46,"props":319,"children":320},{},[321],{"type":33,"value":322},"前端只负责同步展示，不负责最终裁决。",{"type":27,"tag":28,"props":324,"children":326},{"id":325},"_5-saas-的真正护城河常常是后台运营能力而不是首页",[327],{"type":33,"value":328},"5. SaaS 的真正护城河，常常是后台运营能力而不是首页",{"type":27,"tag":36,"props":330,"children":331},{},[332],{"type":33,"value":333},"一个可维护的 SaaS 后台至少要能支持：",{"type":27,"tag":42,"props":335,"children":336},{},[337,342,347,352],{"type":27,"tag":46,"props":338,"children":339},{},[340],{"type":33,"value":341},"用户与 workspace 管理。",{"type":27,"tag":46,"props":343,"children":344},{},[345],{"type":33,"value":346},"订阅状态查看。",{"type":27,"tag":46,"props":348,"children":349},{},[350],{"type":33,"value":351},"异常订单与退款处理。",{"type":27,"tag":46,"props":353,"children":354},{},[355],{"type":33,"value":356},"配额、日志与操作审计。",{"type":27,"tag":36,"props":358,"children":359},{},[360],{"type":33,"value":361},"这些能力如果一开始完全不设计，后期运营成本会远高于功能开发成本。",{"type":27,"tag":28,"props":363,"children":365},{"id":364},"_6-部署与观测要提前进入架构而不是发布前补一层",[366],{"type":33,"value":367},"6. 部署与观测要提前进入架构，而不是发布前补一层",{"type":27,"tag":36,"props":369,"children":370},{},[371],{"type":33,"value":372},"SaaS 项目上线后最常见的问题是：",{"type":27,"tag":42,"props":374,"children":375},{},[376,381,386],{"type":27,"tag":46,"props":377,"children":378},{},[379],{"type":33,"value":380},"某个用户报错，无法定位。",{"type":27,"tag":46,"props":382,"children":383},{},[384],{"type":33,"value":385},"某个套餐功能打不开，不知道是权限还是配置问题。",{"type":27,"tag":46,"props":387,"children":388},{},[389],{"type":33,"value":390},"计费链路失败，但没有统一日志。",{"type":27,"tag":36,"props":392,"children":393},{},[394],{"type":33,"value":395},"因此从早期就要准备：",{"type":27,"tag":42,"props":397,"children":398},{},[399,404,409,414],{"type":27,"tag":46,"props":400,"children":401},{},[402],{"type":33,"value":403},"request id",{"type":27,"tag":46,"props":405,"children":406},{},[407],{"type":33,"value":408},"关键行为日志",{"type":27,"tag":46,"props":410,"children":411},{},[412],{"type":33,"value":413},"订阅事件追踪",{"type":27,"tag":46,"props":415,"children":416},{},[417],{"type":33,"value":418},"错误边界与回滚开关",{"type":27,"tag":28,"props":420,"children":422},{"id":421},"_7-上线前-checklist",[423],{"type":33,"value":424},"7. 上线前 Checklist",{"type":27,"tag":42,"props":426,"children":429},{"className":427},[428],"contains-task-list",[430,443,452,461,470,479,488,497],{"type":27,"tag":46,"props":431,"children":434},{"className":432},[433],"task-list-item",[435,441],{"type":27,"tag":436,"props":437,"children":440},"input",{"disabled":438,"type":439},true,"checkbox",[],{"type":33,"value":442}," User、Workspace、Subscription、Resource 四层对象已分清。",{"type":27,"tag":46,"props":444,"children":446},{"className":445},[433],[447,450],{"type":27,"tag":436,"props":448,"children":449},{"disabled":438,"type":439},[],{"type":33,"value":451}," 认证、授权、计费状态不是混在一起判断。",{"type":27,"tag":46,"props":453,"children":455},{"className":454},[433],[456,459],{"type":27,"tag":436,"props":457,"children":458},{"disabled":438,"type":439},[],{"type":33,"value":460}," 服务端是真正的权限裁决层。",{"type":27,"tag":46,"props":462,"children":464},{"className":463},[433],[465,468],{"type":27,"tag":436,"props":466,"children":467},{"disabled":438,"type":439},[],{"type":33,"value":469}," 后台已能查看用户、套餐、日志与异常订单。",{"type":27,"tag":46,"props":471,"children":473},{"className":472},[433],[474,477],{"type":27,"tag":436,"props":475,"children":476},{"disabled":438,"type":439},[],{"type":33,"value":478}," 关键操作有审计记录与 request id。",{"type":27,"tag":46,"props":480,"children":482},{"className":481},[433],[483,486],{"type":27,"tag":436,"props":484,"children":485},{"disabled":438,"type":439},[],{"type":33,"value":487}," 支付、续费、退款与降级都有状态流转定义。",{"type":27,"tag":46,"props":489,"children":491},{"className":490},[433],[492,495],{"type":27,"tag":436,"props":493,"children":494},{"disabled":438,"type":439},[],{"type":33,"value":496}," 营销站、登录、控制台边界已隔离。",{"type":27,"tag":46,"props":498,"children":500},{"className":499},[433],[501,504],{"type":27,"tag":436,"props":502,"children":503},{"disabled":438,"type":439},[],{"type":33,"value":505}," 已准备最小可回滚的部署方案。",{"type":27,"tag":28,"props":507,"children":509},{"id":508},"faq",[510],{"type":33,"value":511},"FAQ",{"type":27,"tag":513,"props":514,"children":516},"h3",{"id":515},"nextjs-适合做-saas-吗",[517],{"type":33,"value":518},"Next.js 适合做 SaaS 吗？",{"type":27,"tag":36,"props":520,"children":521},{},[522],{"type":33,"value":523},"适合，尤其适合内容站、营销站和控制台共存的产品。但前提是你把认证、权限和数据边界先设计好。",{"type":27,"tag":513,"props":525,"children":527},{"id":526},"saas-一开始一定要做多租户吗",[528],{"type":33,"value":529},"SaaS 一开始一定要做多租户吗？",{"type":27,"tag":36,"props":531,"children":532},{},[533],{"type":33,"value":534},"不一定，但至少要预留 tenant 或 workspace 这层抽象，不然后续升级代价很高。",{"type":27,"tag":513,"props":536,"children":538},{"id":537},"订阅状态只在前端控制可以吗",[539],{"type":33,"value":540},"订阅状态只在前端控制可以吗？",{"type":27,"tag":36,"props":542,"children":543},{},[544],{"type":33,"value":545},"不可以。前端只负责展示，真正的功能开关必须在服务端校验。",{"type":27,"tag":28,"props":547,"children":549},{"id":548},"延伸阅读",[550],{"type":33,"value":548},{"type":27,"tag":42,"props":552,"children":553},{},[554,564,573,582],{"type":27,"tag":46,"props":555,"children":556},{},[557],{"type":27,"tag":558,"props":559,"children":561},"a",{"href":560},"/topics/next/server-actions-complete-guide",[562],{"type":33,"value":563},"Server Actions 完整指南：表单处理、乐观更新与错误恢复",{"type":27,"tag":46,"props":565,"children":566},{},[567],{"type":27,"tag":558,"props":568,"children":570},{"href":569},"/topics/next/cache-strategy-deep-dive",[571],{"type":33,"value":572},"缓存策略深度解析：Next.js App Router 下的数据与页面缓存",{"type":27,"tag":46,"props":574,"children":575},{},[576],{"type":27,"tag":558,"props":577,"children":579},{"href":578},"/topics/next/vercel-deployment-optimization-guide",[580],{"type":33,"value":581},"Vercel 部署深度优化：构建、边缘函数与成本控制",{"type":27,"tag":46,"props":583,"children":584},{},[585],{"type":27,"tag":558,"props":586,"children":588},{"href":587},"/topics/next/nextjs-supabase-practical-guide",[589],{"type":33,"value":590},"Next.js + Supabase 实战：认证、数据库与实时能力怎么接",{"title":7,"searchDepth":592,"depth":592,"links":593},3,[594,596,597,598,599,600,601,602,603,608],{"id":30,"depth":595,"text":34},2,{"id":73,"depth":595,"text":76},{"id":194,"depth":595,"text":197},{"id":223,"depth":595,"text":226},{"id":263,"depth":595,"text":266},{"id":325,"depth":595,"text":328},{"id":364,"depth":595,"text":367},{"id":421,"depth":595,"text":424},{"id":508,"depth":595,"text":511,"children":604},[605,606,607],{"id":515,"depth":592,"text":518},{"id":526,"depth":592,"text":529},{"id":537,"depth":592,"text":540},{"id":548,"depth":595,"text":548},"markdown","content:topics:next:nextjs-fullstack-saas-guide.md","content","topics/next/nextjs-fullstack-saas-guide.md","topics/next/nextjs-fullstack-saas-guide","md",[616,1799,2609],{"_path":569,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":617,"description":618,"date":619,"topic":5,"author":11,"tags":620,"image":626,"featured":438,"readingTime":627,"body":628,"_type":609,"_id":1796,"_source":611,"_file":1797,"_stem":1798,"_extension":614},"缓存策略深度解析","从 HTTP/CDN 到 Next.js App Router 的缓存语义，系统拆解缓存的分层模型、失效策略与常见踩坑，给出可直接落地的工程化方案与检查清单。","2026-01-20",[621,622,13,623,624,625],"缓存","CDN","App Router","revalidateTag","Cache-Control","/images/topics/next/cache-strategy.jpg",22,{"type":24,"children":629,"toc":1762},[630,634,647,652,670,675,693,698,702,708,713,756,761,779,785,798,803,806,812,817,881,887,892,903,908,926,932,937,956,966,969,975,980,993,998,1004,1009,1074,1087,1093,1098,1125,1128,1134,1139,1162,1168,1173,1191,1196,1202,1227,1232,1245,1248,1254,1259,1277,1283,1288,1318,1323,1348,1353,1359,1370,1388,1391,1397,1403,1421,1427,1445,1450,1463,1466,1472,1477,1505,1510,1539,1542,1548,1554,1592,1598,1640,1646,1664,1667,1673,1731,1734,1739,1744],{"type":27,"tag":28,"props":631,"children":632},{"id":617},[633],{"type":33,"value":617},{"type":27,"tag":36,"props":635,"children":636},{},[637,639,645],{"type":33,"value":638},"缓存不是“开关”，而是一套",{"type":27,"tag":640,"props":641,"children":642},"strong",{},[643],{"type":33,"value":644},"分层一致性工程",{"type":33,"value":646},"。",{"type":27,"tag":36,"props":648,"children":649},{},[650],{"type":33,"value":651},"做得好，缓存能同时提升：",{"type":27,"tag":42,"props":653,"children":654},{},[655,660,665],{"type":27,"tag":46,"props":656,"children":657},{},[658],{"type":33,"value":659},"TTFB / LCP（更快）",{"type":27,"tag":46,"props":661,"children":662},{},[663],{"type":33,"value":664},"稳定性（抗突发流量、抗抖动）",{"type":27,"tag":46,"props":666,"children":667},{},[668],{"type":33,"value":669},"成本（更少的源站请求）",{"type":27,"tag":36,"props":671,"children":672},{},[673],{"type":33,"value":674},"做得不好，缓存会制造更可怕的问题：",{"type":27,"tag":42,"props":676,"children":677},{},[678,683,688],{"type":27,"tag":46,"props":679,"children":680},{},[681],{"type":33,"value":682},"用户看到过期数据，且你无法复现",{"type":27,"tag":46,"props":684,"children":685},{},[686],{"type":33,"value":687},"A 用户的内容被 B 用户读到（严重安全事故）",{"type":27,"tag":46,"props":689,"children":690},{},[691],{"type":33,"value":692},"缓存命中率很低，反而更慢、更贵",{"type":27,"tag":36,"props":694,"children":695},{},[696],{"type":33,"value":697},"这篇文章的目标是：让你用一套统一的心智模型，设计并落地“可控、可观测、可回滚”的缓存系统。并以 Next.js App Router 为例，把框架缓存语义纳入整套设计。",{"type":27,"tag":699,"props":700,"children":701},"hr",{},[],{"type":27,"tag":28,"props":703,"children":705},{"id":704},"_1-先建立缓存的分层模型",[706],{"type":33,"value":707},"1. 先建立缓存的“分层模型”",{"type":27,"tag":36,"props":709,"children":710},{},[711],{"type":33,"value":712},"真实系统的缓存几乎总是分层的：",{"type":27,"tag":238,"props":714,"children":715},{},[716,726,736,746],{"type":27,"tag":46,"props":717,"children":718},{},[719,724],{"type":27,"tag":640,"props":720,"children":721},{},[722],{"type":33,"value":723},"浏览器缓存",{"type":33,"value":725},"（Browser Cache）",{"type":27,"tag":46,"props":727,"children":728},{},[729,734],{"type":27,"tag":640,"props":730,"children":731},{},[732],{"type":33,"value":733},"CDN/边缘缓存",{"type":33,"value":735},"（Edge Cache）",{"type":27,"tag":46,"props":737,"children":738},{},[739,744],{"type":27,"tag":640,"props":740,"children":741},{},[742],{"type":33,"value":743},"应用侧缓存",{"type":33,"value":745},"（App Cache：SSR/RSC payload、fetch cache）",{"type":27,"tag":46,"props":747,"children":748},{},[749,754],{"type":27,"tag":640,"props":750,"children":751},{},[752],{"type":33,"value":753},"数据侧缓存",{"type":33,"value":755},"（Redis/DB buffer）",{"type":27,"tag":36,"props":757,"children":758},{},[759],{"type":33,"value":760},"你需要明确：",{"type":27,"tag":42,"props":762,"children":763},{},[764,769,774],{"type":27,"tag":46,"props":765,"children":766},{},[767],{"type":33,"value":768},"每一层缓存什么？（HTML / JSON / 静态资源 / RSC payload）",{"type":27,"tag":46,"props":770,"children":771},{},[772],{"type":33,"value":773},"每一层的 TTL 多久？",{"type":27,"tag":46,"props":775,"children":776},{},[777],{"type":33,"value":778},"写操作发生后，哪几层需要失效？",{"type":27,"tag":513,"props":780,"children":782},{"id":781},"_11-缓存的两类问题性能与一致性",[783],{"type":33,"value":784},"1.1 缓存的两类问题：性能与一致性",{"type":27,"tag":42,"props":786,"children":787},{},[788,793],{"type":27,"tag":46,"props":789,"children":790},{},[791],{"type":33,"value":792},"性能问题：命中率、压缩、复用、减少源站",{"type":27,"tag":46,"props":794,"children":795},{},[796],{"type":33,"value":797},"一致性问题：失效、隔离、权限、版本",{"type":27,"tag":36,"props":799,"children":800},{},[801],{"type":33,"value":802},"很多团队“缓存做不好”，本质是只解决了性能，忽略了一致性。",{"type":27,"tag":699,"props":804,"children":805},{},[],{"type":27,"tag":28,"props":807,"children":809},{"id":808},"_2-http-缓存基础cache-control-的正确用法",[810],{"type":33,"value":811},"2. HTTP 缓存基础：Cache-Control 的正确用法",{"type":27,"tag":36,"props":813,"children":814},{},[815],{"type":33,"value":816},"HTTP 缓存是地基。你至少要掌握这些指令：",{"type":27,"tag":42,"props":818,"children":819},{},[820,837,848,859,870],{"type":27,"tag":46,"props":821,"children":822},{},[823,829,831],{"type":27,"tag":212,"props":824,"children":826},{"className":825},[],[827],{"type":33,"value":828},"public",{"type":33,"value":830}," / ",{"type":27,"tag":212,"props":832,"children":834},{"className":833},[],[835],{"type":33,"value":836},"private",{"type":27,"tag":46,"props":838,"children":839},{},[840,846],{"type":27,"tag":212,"props":841,"children":843},{"className":842},[],[844],{"type":33,"value":845},"max-age",{"type":33,"value":847},"（强缓存 TTL）",{"type":27,"tag":46,"props":849,"children":850},{},[851,857],{"type":27,"tag":212,"props":852,"children":854},{"className":853},[],[855],{"type":33,"value":856},"s-maxage",{"type":33,"value":858},"（CDN 侧 TTL）",{"type":27,"tag":46,"props":860,"children":861},{},[862,868],{"type":27,"tag":212,"props":863,"children":865},{"className":864},[],[866],{"type":33,"value":867},"stale-while-revalidate",{"type":33,"value":869},"（过期可先用旧的，再后台更新）",{"type":27,"tag":46,"props":871,"children":872},{},[873,879],{"type":27,"tag":212,"props":874,"children":876},{"className":875},[],[877],{"type":33,"value":878},"no-store",{"type":33,"value":880},"（不要存储）",{"type":27,"tag":513,"props":882,"children":884},{"id":883},"_21-一个常用的-cdn-缓存头模板",[885],{"type":33,"value":886},"2.1 一个常用的 CDN 缓存头模板",{"type":27,"tag":36,"props":888,"children":889},{},[890],{"type":33,"value":891},"对“公共页面（无用户态）”：",{"type":27,"tag":204,"props":893,"children":898},{"className":894,"code":896,"language":897,"meta":7},[895],"language-http","Cache-Control: public, max-age=0, s-maxage=600, stale-while-revalidate=86400\n","http",[899],{"type":27,"tag":212,"props":900,"children":901},{"__ignoreMap":7},[902],{"type":33,"value":896},{"type":27,"tag":36,"props":904,"children":905},{},[906],{"type":33,"value":907},"含义：",{"type":27,"tag":42,"props":909,"children":910},{},[911,916,921],{"type":27,"tag":46,"props":912,"children":913},{},[914],{"type":33,"value":915},"浏览器不强缓存（max-age=0）",{"type":27,"tag":46,"props":917,"children":918},{},[919],{"type":33,"value":920},"CDN 缓存 10 分钟",{"type":27,"tag":46,"props":922,"children":923},{},[924],{"type":33,"value":925},"CDN 过期后仍可先用旧内容 1 天，同时异步更新",{"type":27,"tag":513,"props":927,"children":929},{"id":928},"_22-用户态页面默认-private-或-no-store",[930],{"type":33,"value":931},"2.2 用户态页面：默认 private 或 no-store",{"type":27,"tag":36,"props":933,"children":934},{},[935],{"type":33,"value":936},"一旦响应与用户身份有关：",{"type":27,"tag":42,"props":938,"children":939},{},[940,951],{"type":27,"tag":46,"props":941,"children":942},{},[943,945],{"type":33,"value":944},"默认：",{"type":27,"tag":212,"props":946,"children":948},{"className":947},[],[949],{"type":33,"value":950},"Cache-Control: private, no-store",{"type":27,"tag":46,"props":952,"children":953},{},[954],{"type":33,"value":955},"必须缓存时：缓存 key 必须包含用户维度（例如 userId/session）且只在应用侧缓存",{"type":27,"tag":36,"props":957,"children":958},{},[959,961],{"type":33,"value":960},"结论：",{"type":27,"tag":640,"props":962,"children":963},{},[964],{"type":33,"value":965},"宁可慢一点，也不要把用户态响应交给 CDN 缓存。",{"type":27,"tag":699,"props":967,"children":968},{},[],{"type":27,"tag":28,"props":970,"children":972},{"id":971},"_3-cdn-缓存命中率与-key-设计",[973],{"type":33,"value":974},"3. CDN 缓存：命中率与 Key 设计",{"type":27,"tag":36,"props":976,"children":977},{},[978],{"type":33,"value":979},"CDN 缓存的核心是：",{"type":27,"tag":42,"props":981,"children":982},{},[983,988],{"type":27,"tag":46,"props":984,"children":985},{},[986],{"type":33,"value":987},"你希望命中（hit），就要让相同内容的请求尽可能“看起来一样”",{"type":27,"tag":46,"props":989,"children":990},{},[991],{"type":33,"value":992},"你希望隔离（safe），就要让不同内容的请求“必须不一样”",{"type":27,"tag":36,"props":994,"children":995},{},[996],{"type":33,"value":997},"这两个目标天然冲突。",{"type":27,"tag":513,"props":999,"children":1001},{"id":1000},"_31-cache-key-里哪些维度必须考虑",[1002],{"type":33,"value":1003},"3.1 Cache Key 里哪些维度必须考虑？",{"type":27,"tag":36,"props":1005,"children":1006},{},[1007],{"type":33,"value":1008},"最常见维度：",{"type":27,"tag":42,"props":1010,"children":1011},{},[1012,1021,1030,1041,1052,1063],{"type":27,"tag":46,"props":1013,"children":1014},{},[1015],{"type":27,"tag":212,"props":1016,"children":1018},{"className":1017},[],[1019],{"type":33,"value":1020},"host",{"type":27,"tag":46,"props":1022,"children":1023},{},[1024],{"type":27,"tag":212,"props":1025,"children":1027},{"className":1026},[],[1028],{"type":33,"value":1029},"path",{"type":27,"tag":46,"props":1031,"children":1032},{},[1033,1039],{"type":27,"tag":212,"props":1034,"children":1036},{"className":1035},[],[1037],{"type":33,"value":1038},"query",{"type":33,"value":1040},"（是否纳入 key？）",{"type":27,"tag":46,"props":1042,"children":1043},{},[1044,1050],{"type":27,"tag":212,"props":1045,"children":1047},{"className":1046},[],[1048],{"type":33,"value":1049},"accept-encoding",{"type":33,"value":1051},"（gzip/br）",{"type":27,"tag":46,"props":1053,"children":1054},{},[1055,1061],{"type":27,"tag":212,"props":1056,"children":1058},{"className":1057},[],[1059],{"type":33,"value":1060},"accept-language",{"type":33,"value":1062},"（多语言站点）",{"type":27,"tag":46,"props":1064,"children":1065},{},[1066,1072],{"type":27,"tag":212,"props":1067,"children":1069},{"className":1068},[],[1070],{"type":33,"value":1071},"device",{"type":33,"value":1073},"（移动/桌面，如果你做了 UA vary）",{"type":27,"tag":36,"props":1075,"children":1076},{},[1077,1079,1085],{"type":33,"value":1078},"反例：把“跟内容无关的 query”放进 key（例如 ",{"type":27,"tag":212,"props":1080,"children":1082},{"className":1081},[],[1083],{"type":33,"value":1084},"utm_*",{"type":33,"value":1086},"），会把命中率直接打碎。",{"type":27,"tag":513,"props":1088,"children":1090},{"id":1089},"_32-解决-utm-污染规范化-url",[1091],{"type":33,"value":1092},"3.2 解决 UTM 污染：规范化 URL",{"type":27,"tag":36,"props":1094,"children":1095},{},[1096],{"type":33,"value":1097},"建议：",{"type":27,"tag":42,"props":1099,"children":1100},{},[1101,1113],{"type":27,"tag":46,"props":1102,"children":1103},{},[1104,1106,1111],{"type":33,"value":1105},"进入应用时把 ",{"type":27,"tag":212,"props":1107,"children":1109},{"className":1108},[],[1110],{"type":33,"value":1084},{"type":33,"value":1112}," 归一化并 301 到干净 URL",{"type":27,"tag":46,"props":1114,"children":1115},{},[1116,1118,1123],{"type":33,"value":1117},"或在 CDN 层将 ",{"type":27,"tag":212,"props":1119,"children":1121},{"className":1120},[],[1122],{"type":33,"value":1084},{"type":33,"value":1124}," 从 cache key 里剔除",{"type":27,"tag":699,"props":1126,"children":1127},{},[],{"type":27,"tag":28,"props":1129,"children":1131},{"id":1130},"_4-应用侧缓存nextjs-app-router-的缓存语义",[1132],{"type":33,"value":1133},"4. 应用侧缓存：Next.js App Router 的缓存语义",{"type":27,"tag":36,"props":1135,"children":1136},{},[1137],{"type":33,"value":1138},"App Router 的“缓存”不是一个地方，而是一套语义：",{"type":27,"tag":42,"props":1140,"children":1141},{},[1142,1147,1152,1157],{"type":27,"tag":46,"props":1143,"children":1144},{},[1145],{"type":33,"value":1146},"fetch 结果缓存",{"type":27,"tag":46,"props":1148,"children":1149},{},[1150],{"type":33,"value":1151},"RSC payload 缓存",{"type":27,"tag":46,"props":1153,"children":1154},{},[1155],{"type":33,"value":1156},"route segment 缓存",{"type":27,"tag":46,"props":1158,"children":1159},{},[1160],{"type":33,"value":1161},"revalidate 机制（path/tag）",{"type":27,"tag":513,"props":1163,"children":1165},{"id":1164},"_41-你必须区分静态动态按需再验证",[1166],{"type":33,"value":1167},"4.1 你必须区分：静态、动态、按需再验证",{"type":27,"tag":36,"props":1169,"children":1170},{},[1171],{"type":33,"value":1172},"在 App Router 体系里，一个页面/段可以是：",{"type":27,"tag":42,"props":1174,"children":1175},{},[1176,1181,1186],{"type":27,"tag":46,"props":1177,"children":1178},{},[1179],{"type":33,"value":1180},"静态（长 TTL）",{"type":27,"tag":46,"props":1182,"children":1183},{},[1184],{"type":33,"value":1185},"动态（每次都算）",{"type":27,"tag":46,"props":1187,"children":1188},{},[1189],{"type":33,"value":1190},"ISR/按需再验证（写操作后失效）",{"type":27,"tag":36,"props":1192,"children":1193},{},[1194],{"type":33,"value":1195},"不要把“页面慢”简单归因给 SSR；很多时候是缓存策略没设计好。",{"type":27,"tag":513,"props":1197,"children":1199},{"id":1198},"_42-两个最重要的失效-apirevalidatepath-revalidatetag",[1200],{"type":33,"value":1201},"4.2 两个最重要的失效 API：revalidatePath / revalidateTag",{"type":27,"tag":42,"props":1203,"children":1204},{},[1205,1216],{"type":27,"tag":46,"props":1206,"children":1207},{},[1208,1214],{"type":27,"tag":212,"props":1209,"children":1211},{"className":1210},[],[1212],{"type":33,"value":1213},"revalidatePath('/xxx')",{"type":33,"value":1215},"：按路由失效（粗粒度）",{"type":27,"tag":46,"props":1217,"children":1218},{},[1219,1225],{"type":27,"tag":212,"props":1220,"children":1222},{"className":1221},[],[1223],{"type":33,"value":1224},"revalidateTag('tag')",{"type":33,"value":1226},"：按数据域失效（细粒度）",{"type":27,"tag":36,"props":1228,"children":1229},{},[1230],{"type":33,"value":1231},"经验法则：",{"type":27,"tag":42,"props":1233,"children":1234},{},[1235,1240],{"type":27,"tag":46,"props":1236,"children":1237},{},[1238],{"type":33,"value":1239},"数据只影响单页：用 path",{"type":27,"tag":46,"props":1241,"children":1242},{},[1243],{"type":33,"value":1244},"数据影响多页（列表、详情、侧栏统计）：用 tag",{"type":27,"tag":699,"props":1246,"children":1247},{},[],{"type":27,"tag":28,"props":1249,"children":1251},{"id":1250},"_5-写操作后的失效策略从拍脑袋到可证明",[1252],{"type":33,"value":1253},"5. 写操作后的失效策略：从“拍脑袋”到“可证明”",{"type":27,"tag":36,"props":1255,"children":1256},{},[1257],{"type":33,"value":1258},"写操作发生后，你要回答：",{"type":27,"tag":42,"props":1260,"children":1261},{},[1262,1267,1272],{"type":27,"tag":46,"props":1263,"children":1264},{},[1265],{"type":33,"value":1266},"哪些页面会受影响？",{"type":27,"tag":46,"props":1268,"children":1269},{},[1270],{"type":33,"value":1271},"哪些数据域会受影响？",{"type":27,"tag":46,"props":1273,"children":1274},{},[1275],{"type":33,"value":1276},"这些受影响对象是否有统一 tag？",{"type":27,"tag":513,"props":1278,"children":1280},{"id":1279},"_51-建议采用域驱动-tag",[1281],{"type":33,"value":1282},"5.1 建议采用“域驱动 tag”",{"type":27,"tag":36,"props":1284,"children":1285},{},[1286],{"type":33,"value":1287},"例子：电商商品",{"type":27,"tag":42,"props":1289,"children":1290},{},[1291,1300,1309],{"type":27,"tag":46,"props":1292,"children":1293},{},[1294],{"type":27,"tag":212,"props":1295,"children":1297},{"className":1296},[],[1298],{"type":33,"value":1299},"product:{id}",{"type":27,"tag":46,"props":1301,"children":1302},{},[1303],{"type":27,"tag":212,"props":1304,"children":1306},{"className":1305},[],[1307],{"type":33,"value":1308},"product:list",{"type":27,"tag":46,"props":1310,"children":1311},{},[1312],{"type":27,"tag":212,"props":1313,"children":1315},{"className":1314},[],[1316],{"type":33,"value":1317},"inventory:{id}",{"type":27,"tag":36,"props":1319,"children":1320},{},[1321],{"type":33,"value":1322},"当你更新库存时：",{"type":27,"tag":42,"props":1324,"children":1325},{},[1326,1336],{"type":27,"tag":46,"props":1327,"children":1328},{},[1329,1331],{"type":33,"value":1330},"失效 ",{"type":27,"tag":212,"props":1332,"children":1334},{"className":1333},[],[1335],{"type":33,"value":1317},{"type":27,"tag":46,"props":1337,"children":1338},{},[1339,1341,1346],{"type":33,"value":1340},"可能也失效 ",{"type":27,"tag":212,"props":1342,"children":1344},{"className":1343},[],[1345],{"type":33,"value":1299},{"type":33,"value":1347},"（如果详情页展示库存）",{"type":27,"tag":36,"props":1349,"children":1350},{},[1351],{"type":33,"value":1352},"这样你可以把失效做成一套规则，而不是散落在业务代码里。",{"type":27,"tag":513,"props":1354,"children":1356},{"id":1355},"_52-不要用全站失效当默认",[1357],{"type":33,"value":1358},"5.2 不要用“全站失效”当默认",{"type":27,"tag":36,"props":1360,"children":1361},{},[1362,1368],{"type":27,"tag":212,"props":1363,"children":1365},{"className":1364},[],[1366],{"type":33,"value":1367},"revalidatePath('/')",{"type":33,"value":1369},"、清 CDN 全站缓存，短期看省事，长期一定出问题：",{"type":27,"tag":42,"props":1371,"children":1372},{},[1373,1378,1383],{"type":27,"tag":46,"props":1374,"children":1375},{},[1376],{"type":33,"value":1377},"命中率暴跌",{"type":27,"tag":46,"props":1379,"children":1380},{},[1381],{"type":33,"value":1382},"源站被打穿",{"type":27,"tag":46,"props":1384,"children":1385},{},[1386],{"type":33,"value":1387},"回归定位困难",{"type":27,"tag":699,"props":1389,"children":1390},{},[],{"type":27,"tag":28,"props":1392,"children":1394},{"id":1393},"_6-缓存与权限最危险的组合",[1395],{"type":33,"value":1396},"6. 缓存与权限：最危险的组合",{"type":27,"tag":513,"props":1398,"children":1400},{"id":1399},"_61-三条硬规则",[1401],{"type":33,"value":1402},"6.1 三条硬规则",{"type":27,"tag":42,"props":1404,"children":1405},{},[1406,1411,1416],{"type":27,"tag":46,"props":1407,"children":1408},{},[1409],{"type":33,"value":1410},"用户态响应不要进 CDN",{"type":27,"tag":46,"props":1412,"children":1413},{},[1414],{"type":33,"value":1415},"缓存 key 必须包含权限维度（userId/role）",{"type":27,"tag":46,"props":1417,"children":1418},{},[1419],{"type":33,"value":1420},"SSR 页面要明确区分“公共数据”与“私有数据”",{"type":27,"tag":513,"props":1422,"children":1424},{"id":1423},"_62-常见事故模式",[1425],{"type":33,"value":1426},"6.2 常见事故模式",{"type":27,"tag":42,"props":1428,"children":1429},{},[1430,1435,1440],{"type":27,"tag":46,"props":1431,"children":1432},{},[1433],{"type":33,"value":1434},"详情页同时展示公共内容 + 用户私有信息（例如是否收藏）",{"type":27,"tag":46,"props":1436,"children":1437},{},[1438],{"type":33,"value":1439},"你把整页缓存了",{"type":27,"tag":46,"props":1441,"children":1442},{},[1443],{"type":33,"value":1444},"结果：用户私有信息被缓存并泄漏",{"type":27,"tag":36,"props":1446,"children":1447},{},[1448],{"type":33,"value":1449},"正确做法：",{"type":27,"tag":42,"props":1451,"children":1452},{},[1453,1458],{"type":27,"tag":46,"props":1454,"children":1455},{},[1456],{"type":33,"value":1457},"公共内容可缓存",{"type":27,"tag":46,"props":1459,"children":1460},{},[1461],{"type":33,"value":1462},"私有信息走客户端请求或单独的私有 API（不缓存）",{"type":27,"tag":699,"props":1464,"children":1465},{},[],{"type":27,"tag":28,"props":1467,"children":1469},{"id":1468},"_7-可观测性让缓存变成可量化系统",[1470],{"type":33,"value":1471},"7. 可观测性：让缓存变成“可量化系统”",{"type":27,"tag":36,"props":1473,"children":1474},{},[1475],{"type":33,"value":1476},"你至少需要这些指标：",{"type":27,"tag":42,"props":1478,"children":1479},{},[1480,1485,1490,1495,1500],{"type":27,"tag":46,"props":1481,"children":1482},{},[1483],{"type":33,"value":1484},"CDN 命中率（hit/miss）",{"type":27,"tag":46,"props":1486,"children":1487},{},[1488],{"type":33,"value":1489},"源站 QPS",{"type":27,"tag":46,"props":1491,"children":1492},{},[1493],{"type":33,"value":1494},"TTFB 分布（p50/p95）",{"type":27,"tag":46,"props":1496,"children":1497},{},[1498],{"type":33,"value":1499},"revalidate 调用量与耗时",{"type":27,"tag":46,"props":1501,"children":1502},{},[1503],{"type":33,"value":1504},"失效失败率",{"type":27,"tag":36,"props":1506,"children":1507},{},[1508],{"type":33,"value":1509},"日志建议：",{"type":27,"tag":42,"props":1511,"children":1512},{},[1513,1526],{"type":27,"tag":46,"props":1514,"children":1515},{},[1516,1518,1524],{"type":33,"value":1517},"给每个请求注入 ",{"type":27,"tag":212,"props":1519,"children":1521},{"className":1520},[],[1522],{"type":33,"value":1523},"cacheStatus",{"type":33,"value":1525},"（HIT/MISS/BYPASS）",{"type":27,"tag":46,"props":1527,"children":1528},{},[1529,1531,1537],{"type":33,"value":1530},"记录 ",{"type":27,"tag":212,"props":1532,"children":1534},{"className":1533},[],[1535],{"type":33,"value":1536},"cacheKey",{"type":33,"value":1538}," 的关键维度（注意脱敏）",{"type":27,"tag":699,"props":1540,"children":1541},{},[],{"type":27,"tag":28,"props":1543,"children":1545},{"id":1544},"_8-一份可直接执行的缓存策略模板",[1546],{"type":33,"value":1547},"8. 一份可直接执行的缓存策略模板",{"type":27,"tag":513,"props":1549,"children":1551},{"id":1550},"_81-公共内容页文章落地页",[1552],{"type":33,"value":1553},"8.1 公共内容页（文章、落地页）",{"type":27,"tag":42,"props":1555,"children":1556},{},[1557,1568,1581],{"type":27,"tag":46,"props":1558,"children":1559},{},[1560,1562],{"type":33,"value":1561},"CDN：",{"type":27,"tag":212,"props":1563,"children":1565},{"className":1564},[],[1566],{"type":33,"value":1567},"s-maxage=600, stale-while-revalidate=86400",{"type":27,"tag":46,"props":1569,"children":1570},{},[1571,1573,1579],{"type":33,"value":1572},"App：允许 fetch cache，tag 以 ",{"type":27,"tag":212,"props":1574,"children":1576},{"className":1575},[],[1577],{"type":33,"value":1578},"content:*",{"type":33,"value":1580}," 组织",{"type":27,"tag":46,"props":1582,"children":1583},{},[1584,1586],{"type":33,"value":1585},"写操作：发布文章时 ",{"type":27,"tag":212,"props":1587,"children":1589},{"className":1588},[],[1590],{"type":33,"value":1591},"revalidateTag('content:article')",{"type":27,"tag":513,"props":1593,"children":1595},{"id":1594},"_82-列表-详情强一致性要求不高",[1596],{"type":33,"value":1597},"8.2 列表 + 详情（强一致性要求不高）",{"type":27,"tag":42,"props":1599,"children":1600},{},[1601,1612,1623],{"type":27,"tag":46,"props":1602,"children":1603},{},[1604,1606],{"type":33,"value":1605},"详情：tag ",{"type":27,"tag":212,"props":1607,"children":1609},{"className":1608},[],[1610],{"type":33,"value":1611},"entity:{id}",{"type":27,"tag":46,"props":1613,"children":1614},{},[1615,1617],{"type":33,"value":1616},"列表：tag ",{"type":27,"tag":212,"props":1618,"children":1620},{"className":1619},[],[1621],{"type":33,"value":1622},"entity:list",{"type":27,"tag":46,"props":1624,"children":1625},{},[1626,1628,1633,1635],{"type":33,"value":1627},"更新：失效 ",{"type":27,"tag":212,"props":1629,"children":1631},{"className":1630},[],[1632],{"type":33,"value":1611},{"type":33,"value":1634}," + ",{"type":27,"tag":212,"props":1636,"children":1638},{"className":1637},[],[1639],{"type":33,"value":1622},{"type":27,"tag":513,"props":1641,"children":1643},{"id":1642},"_83-强一致性库存余额",[1644],{"type":33,"value":1645},"8.3 强一致性（库存、余额）",{"type":27,"tag":42,"props":1647,"children":1648},{},[1649,1654,1659],{"type":27,"tag":46,"props":1650,"children":1651},{},[1652],{"type":33,"value":1653},"默认不缓存",{"type":27,"tag":46,"props":1655,"children":1656},{},[1657],{"type":33,"value":1658},"或只做极短 TTL（1~3s）",{"type":27,"tag":46,"props":1660,"children":1661},{},[1662],{"type":33,"value":1663},"必须有幂等与容错",{"type":27,"tag":699,"props":1665,"children":1666},{},[],{"type":27,"tag":28,"props":1668,"children":1670},{"id":1669},"_9-检查清单上线前必过",[1671],{"type":33,"value":1672},"9. 检查清单（上线前必过）",{"type":27,"tag":42,"props":1674,"children":1676},{"className":1675},[428],[1677,1686,1695,1704,1713,1722],{"type":27,"tag":46,"props":1678,"children":1680},{"className":1679},[433],[1681,1684],{"type":27,"tag":436,"props":1682,"children":1683},{"disabled":438,"type":439},[],{"type":33,"value":1685}," 哪些路由是 public/private？是否明确",{"type":27,"tag":46,"props":1687,"children":1689},{"className":1688},[433],[1690,1693],{"type":27,"tag":436,"props":1691,"children":1692},{"disabled":438,"type":439},[],{"type":33,"value":1694}," CDN cache key 是否会被无关 query 污染",{"type":27,"tag":46,"props":1696,"children":1698},{"className":1697},[433],[1699,1702],{"type":27,"tag":436,"props":1700,"children":1701},{"disabled":438,"type":439},[],{"type":33,"value":1703}," 用户态响应是否有 no-store",{"type":27,"tag":46,"props":1705,"children":1707},{"className":1706},[433],[1708,1711],{"type":27,"tag":436,"props":1709,"children":1710},{"disabled":438,"type":439},[],{"type":33,"value":1712}," 写操作是否有明确的 tag/path 失效策略",{"type":27,"tag":46,"props":1714,"children":1716},{"className":1715},[433],[1717,1720],{"type":27,"tag":436,"props":1718,"children":1719},{"disabled":438,"type":439},[],{"type":33,"value":1721}," 是否有命中率与回源监控",{"type":27,"tag":46,"props":1723,"children":1725},{"className":1724},[433],[1726,1729],{"type":27,"tag":436,"props":1727,"children":1728},{"disabled":438,"type":439},[],{"type":33,"value":1730}," 回滚策略是什么（关缓存、降级）",{"type":27,"tag":699,"props":1732,"children":1733},{},[],{"type":27,"tag":28,"props":1735,"children":1737},{"id":1736},"总结",[1738],{"type":33,"value":1736},{"type":27,"tag":36,"props":1740,"children":1741},{},[1742],{"type":33,"value":1743},"缓存策略的本质是：",{"type":27,"tag":42,"props":1745,"children":1746},{},[1747,1752,1757],{"type":27,"tag":46,"props":1748,"children":1749},{},[1750],{"type":33,"value":1751},"把“快”变成可控的工程系统",{"type":27,"tag":46,"props":1753,"children":1754},{},[1755],{"type":33,"value":1756},"把“一致性”变成可证明的规则",{"type":27,"tag":46,"props":1758,"children":1759},{},[1760],{"type":33,"value":1761},"把“失效”变成可观测、可回滚的流程",{"title":7,"searchDepth":592,"depth":592,"links":1763},[1764,1765,1768,1772,1776,1780,1784,1788,1789,1794,1795],{"id":617,"depth":595,"text":617},{"id":704,"depth":595,"text":707,"children":1766},[1767],{"id":781,"depth":592,"text":784},{"id":808,"depth":595,"text":811,"children":1769},[1770,1771],{"id":883,"depth":592,"text":886},{"id":928,"depth":592,"text":931},{"id":971,"depth":595,"text":974,"children":1773},[1774,1775],{"id":1000,"depth":592,"text":1003},{"id":1089,"depth":592,"text":1092},{"id":1130,"depth":595,"text":1133,"children":1777},[1778,1779],{"id":1164,"depth":592,"text":1167},{"id":1198,"depth":592,"text":1201},{"id":1250,"depth":595,"text":1253,"children":1781},[1782,1783],{"id":1279,"depth":592,"text":1282},{"id":1355,"depth":592,"text":1358},{"id":1393,"depth":595,"text":1396,"children":1785},[1786,1787],{"id":1399,"depth":592,"text":1402},{"id":1423,"depth":592,"text":1426},{"id":1468,"depth":595,"text":1471},{"id":1544,"depth":595,"text":1547,"children":1790},[1791,1792,1793],{"id":1550,"depth":592,"text":1553},{"id":1594,"depth":592,"text":1597},{"id":1642,"depth":592,"text":1645},{"id":1669,"depth":595,"text":1672},{"id":1736,"depth":595,"text":1736},"content:topics:next:cache-strategy-deep-dive.md","topics/next/cache-strategy-deep-dive.md","topics/next/cache-strategy-deep-dive",{"_path":1800,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1801,"description":1802,"date":619,"topic":5,"author":11,"tags":1803,"image":1807,"featured":438,"readingTime":627,"body":1808,"_type":609,"_id":2606,"_source":611,"_file":2607,"_stem":2608,"_extension":614},"/topics/next/isr-practice","增量静态再生 (ISR) 实战","从缓存语义与一致性边界出发，讲清 ISR 的原理、适用场景与落地细节，并给出可直接复用的页面设计、失效策略与线上排障方法。",[13,1804,621,1805,1806],"ISR","SSG","SEO","/images/topics/next/isr.jpg",{"type":24,"children":1809,"toc":2581},[1810,1815,1820,1831,1836,1849,1854,1882,1885,1891,1896,1929,1934,1937,1943,1948,1961,1966,1979,1982,1988,1994,1999,2017,2022,2035,2041,2045,2063,2067,2085,2088,2094,2100,2105,2110,2123,2129,2134,2139,2152,2156,2169,2172,2178,2183,2188,2201,2206,2224,2227,2233,2238,2274,2279,2304,2309,2312,2318,2324,2329,2347,2353,2371,2377,2382,2400,2406,2419,2425,2438,2442,2455,2461,2466,2471,2484,2487,2493,2551,2554,2558,2563],{"type":27,"tag":28,"props":1811,"children":1813},{"id":1812},"增量静态再生-isr-实战",[1814],{"type":33,"value":1801},{"type":27,"tag":36,"props":1816,"children":1817},{},[1818],{"type":33,"value":1819},"ISR（Incremental Static Regeneration）经常被误解为“更聪明的 SSG”。",{"type":27,"tag":36,"props":1821,"children":1822},{},[1823,1825,1830],{"type":33,"value":1824},"更准确的说法是：ISR 是一套把“静态化收益”与“内容更新需求”折中起来的",{"type":27,"tag":640,"props":1826,"children":1827},{},[1828],{"type":33,"value":1829},"缓存与再生成协议",{"type":33,"value":646},{"type":27,"tag":36,"props":1832,"children":1833},{},[1834],{"type":33,"value":1835},"它解决的核心矛盾是：",{"type":27,"tag":42,"props":1837,"children":1838},{},[1839,1844],{"type":27,"tag":46,"props":1840,"children":1841},{},[1842],{"type":33,"value":1843},"你想要静态页面的性能与稳定性（低 TTFB、CDN 命中、高并发能力）",{"type":27,"tag":46,"props":1845,"children":1846},{},[1847],{"type":33,"value":1848},"你又希望内容能及时更新（分钟级甚至秒级）",{"type":27,"tag":36,"props":1850,"children":1851},{},[1852],{"type":33,"value":1853},"这篇文章按“上生产能跑稳”为标准，讲清：",{"type":27,"tag":42,"props":1855,"children":1856},{},[1857,1862,1867,1872,1877],{"type":27,"tag":46,"props":1858,"children":1859},{},[1860],{"type":33,"value":1861},"ISR 的工作原理与心智模型",{"type":27,"tag":46,"props":1863,"children":1864},{},[1865],{"type":33,"value":1866},"适用场景与反例",{"type":27,"tag":46,"props":1868,"children":1869},{},[1870],{"type":33,"value":1871},"页面设计（列表/详情/搜索）",{"type":27,"tag":46,"props":1873,"children":1874},{},[1875],{"type":33,"value":1876},"失效策略：时间驱动 vs 事件驱动",{"type":27,"tag":46,"props":1878,"children":1879},{},[1880],{"type":33,"value":1881},"线上排障：为什么更新不生效/为什么频繁回源",{"type":27,"tag":699,"props":1883,"children":1884},{},[],{"type":27,"tag":28,"props":1886,"children":1888},{"id":1887},"_1-先统一语言你到底想解决什么",[1889],{"type":33,"value":1890},"1. 先统一语言：你到底想解决什么？",{"type":27,"tag":36,"props":1892,"children":1893},{},[1894],{"type":33,"value":1895},"在真实业务里，“内容更新”通常有三种等级：",{"type":27,"tag":238,"props":1897,"children":1898},{},[1899,1909,1919],{"type":27,"tag":46,"props":1900,"children":1901},{},[1902,1907],{"type":27,"tag":640,"props":1903,"children":1904},{},[1905],{"type":33,"value":1906},"允许延迟",{"type":33,"value":1908},"：比如文章页、帮助文档、营销页，更新延迟 5~30 分钟都能接受",{"type":27,"tag":46,"props":1910,"children":1911},{},[1912,1917],{"type":27,"tag":640,"props":1913,"children":1914},{},[1915],{"type":33,"value":1916},"弱一致",{"type":33,"value":1918},"：比如商品价格、库存展示（可能需要更短 TTL，但可以接受短时间旧值）",{"type":27,"tag":46,"props":1920,"children":1921},{},[1922,1927],{"type":27,"tag":640,"props":1923,"children":1924},{},[1925],{"type":33,"value":1926},"强一致",{"type":33,"value":1928},"：比如支付结果、权限信息，不能用 ISR 兜底",{"type":27,"tag":36,"props":1930,"children":1931},{},[1932],{"type":33,"value":1933},"ISR 最适合第 1 类，谨慎用于第 2 类，不适合第 3 类。",{"type":27,"tag":699,"props":1935,"children":1936},{},[],{"type":27,"tag":28,"props":1938,"children":1940},{"id":1939},"_2-isr-的心智模型把它当成cdn-后的再生成缓存",[1941],{"type":33,"value":1942},"2. ISR 的心智模型：把它当成“CDN 后的再生成缓存”",{"type":27,"tag":36,"props":1944,"children":1945},{},[1946],{"type":33,"value":1947},"你可以把 ISR 看作两层：",{"type":27,"tag":42,"props":1949,"children":1950},{},[1951,1956],{"type":27,"tag":46,"props":1952,"children":1953},{},[1954],{"type":33,"value":1955},"CDN：缓存最终 HTML（或 RSC payload）",{"type":27,"tag":46,"props":1957,"children":1958},{},[1959],{"type":33,"value":1960},"应用（Next.js）：缓存页面生成结果，并按规则触发再生成",{"type":27,"tag":36,"props":1962,"children":1963},{},[1964],{"type":33,"value":1965},"关键点：",{"type":27,"tag":42,"props":1967,"children":1968},{},[1969,1974],{"type":27,"tag":46,"props":1970,"children":1971},{},[1972],{"type":33,"value":1973},"ISR 不是“每次请求都重新生成”，而是“绝大多数时候直接命中缓存”",{"type":27,"tag":46,"props":1975,"children":1976},{},[1977],{"type":33,"value":1978},"再生成是“受控发生”的",{"type":27,"tag":699,"props":1980,"children":1981},{},[],{"type":27,"tag":28,"props":1983,"children":1985},{"id":1984},"_3-两种驱动方式时间驱动-vs-事件驱动",[1986],{"type":33,"value":1987},"3. 两种驱动方式：时间驱动 vs 事件驱动",{"type":27,"tag":513,"props":1989,"children":1991},{"id":1990},"_31-时间驱动revalidate-秒数",[1992],{"type":33,"value":1993},"3.1 时间驱动（revalidate 秒数）",{"type":27,"tag":36,"props":1995,"children":1996},{},[1997],{"type":33,"value":1998},"特点：",{"type":27,"tag":42,"props":2000,"children":2001},{},[2002,2007,2012],{"type":27,"tag":46,"props":2003,"children":2004},{},[2005],{"type":33,"value":2006},"简单",{"type":27,"tag":46,"props":2008,"children":2009},{},[2010],{"type":33,"value":2011},"稳定",{"type":27,"tag":46,"props":2013,"children":2014},{},[2015],{"type":33,"value":2016},"但更新延迟不可控（取决于 TTL 与访问触发）",{"type":27,"tag":36,"props":2018,"children":2019},{},[2020],{"type":33,"value":2021},"适用：",{"type":27,"tag":42,"props":2023,"children":2024},{},[2025,2030],{"type":27,"tag":46,"props":2026,"children":2027},{},[2028],{"type":33,"value":2029},"内容更新不频繁",{"type":27,"tag":46,"props":2031,"children":2032},{},[2033],{"type":33,"value":2034},"不需要“发布即生效”",{"type":27,"tag":513,"props":2036,"children":2038},{"id":2037},"_32-事件驱动按需再验证-webhook",[2039],{"type":33,"value":2040},"3.2 事件驱动（按需再验证 / webhook）",{"type":27,"tag":36,"props":2042,"children":2043},{},[2044],{"type":33,"value":1998},{"type":27,"tag":42,"props":2046,"children":2047},{},[2048,2053,2058],{"type":27,"tag":46,"props":2049,"children":2050},{},[2051],{"type":33,"value":2052},"发布后可快速生效",{"type":27,"tag":46,"props":2054,"children":2055},{},[2056],{"type":33,"value":2057},"更可控",{"type":27,"tag":46,"props":2059,"children":2060},{},[2061],{"type":33,"value":2062},"需要你有“内容发布事件”与安全机制",{"type":27,"tag":36,"props":2064,"children":2065},{},[2066],{"type":33,"value":2021},{"type":27,"tag":42,"props":2068,"children":2069},{},[2070,2075,2080],{"type":27,"tag":46,"props":2071,"children":2072},{},[2073],{"type":33,"value":2074},"CMS 发布文章",{"type":27,"tag":46,"props":2076,"children":2077},{},[2078],{"type":33,"value":2079},"商品信息变更",{"type":27,"tag":46,"props":2081,"children":2082},{},[2083],{"type":33,"value":2084},"专题页更新",{"type":27,"tag":699,"props":2086,"children":2087},{},[],{"type":27,"tag":28,"props":2089,"children":2091},{"id":2090},"_4-典型页面怎么做列表-详情",[2092],{"type":33,"value":2093},"4. 典型页面怎么做：列表 + 详情",{"type":27,"tag":513,"props":2095,"children":2097},{"id":2096},"_41-详情页优先事件驱动",[2098],{"type":33,"value":2099},"4.1 详情页：优先事件驱动",{"type":27,"tag":36,"props":2101,"children":2102},{},[2103],{"type":33,"value":2104},"详情页更新的最佳体验通常是“发布即生效”。",{"type":27,"tag":36,"props":2106,"children":2107},{},[2108],{"type":33,"value":2109},"策略：",{"type":27,"tag":42,"props":2111,"children":2112},{},[2113,2118],{"type":27,"tag":46,"props":2114,"children":2115},{},[2116],{"type":33,"value":2117},"详情页使用 ISR（或静态 + 按需再验证）",{"type":27,"tag":46,"props":2119,"children":2120},{},[2121],{"type":33,"value":2122},"CMS 发布时调用 webhook，让应用对特定 path/tag 失效",{"type":27,"tag":513,"props":2124,"children":2126},{"id":2125},"_42-列表页注意聚合页放大效应",[2127],{"type":33,"value":2128},"4.2 列表页：注意“聚合页放大效应”",{"type":27,"tag":36,"props":2130,"children":2131},{},[2132],{"type":33,"value":2133},"列表页（例如文章列表、商品列表）会被大量访问，且它的内容聚合了很多详情。",{"type":27,"tag":36,"props":2135,"children":2136},{},[2137],{"type":33,"value":2138},"常见坑：",{"type":27,"tag":42,"props":2140,"children":2141},{},[2142,2147],{"type":27,"tag":46,"props":2143,"children":2144},{},[2145],{"type":33,"value":2146},"每次新增一篇文章都要失效列表页",{"type":27,"tag":46,"props":2148,"children":2149},{},[2150],{"type":33,"value":2151},"导致列表页频繁再生成 → 回源增多",{"type":27,"tag":36,"props":2153,"children":2154},{},[2155],{"type":33,"value":1097},{"type":27,"tag":42,"props":2157,"children":2158},{},[2159,2164],{"type":27,"tag":46,"props":2160,"children":2161},{},[2162],{"type":33,"value":2163},"列表页采用较长 TTL（例如 10~30 分钟）",{"type":27,"tag":46,"props":2165,"children":2166},{},[2167],{"type":33,"value":2168},"或把“最新内容”拆成单独模块，使用客户端请求/边缘 KV",{"type":27,"tag":699,"props":2170,"children":2171},{},[],{"type":27,"tag":28,"props":2173,"children":2175},{"id":2174},"_5-数据获取与缓存别把-isr-做成慢-ssr",[2176],{"type":33,"value":2177},"5. 数据获取与缓存：别把 ISR 做成“慢 SSR”",{"type":27,"tag":36,"props":2179,"children":2180},{},[2181],{"type":33,"value":2182},"ISR 的性能收益来自“生成一次 → 复用很多次”。",{"type":27,"tag":36,"props":2184,"children":2185},{},[2186],{"type":33,"value":2187},"所以你要避免：",{"type":27,"tag":42,"props":2189,"children":2190},{},[2191,2196],{"type":27,"tag":46,"props":2192,"children":2193},{},[2194],{"type":33,"value":2195},"页面生成时做大量慢查询",{"type":27,"tag":46,"props":2197,"children":2198},{},[2199],{"type":33,"value":2200},"页面生成时串行请求多个上游",{"type":27,"tag":36,"props":2202,"children":2203},{},[2204],{"type":33,"value":2205},"实践建议：",{"type":27,"tag":42,"props":2207,"children":2208},{},[2209,2214,2219],{"type":27,"tag":46,"props":2210,"children":2211},{},[2212],{"type":33,"value":2213},"生成路径尽量轻（prefetch、批量接口、缓存）",{"type":27,"tag":46,"props":2215,"children":2216},{},[2217],{"type":33,"value":2218},"对上游请求设置超时",{"type":27,"tag":46,"props":2220,"children":2221},{},[2222],{"type":33,"value":2223},"做并行请求",{"type":27,"tag":699,"props":2225,"children":2226},{},[],{"type":27,"tag":28,"props":2228,"children":2230},{"id":2229},"_6-失效策略设计从靠感觉到有规则",[2231],{"type":33,"value":2232},"6. 失效策略设计：从“靠感觉”到“有规则”",{"type":27,"tag":36,"props":2234,"children":2235},{},[2236],{"type":33,"value":2237},"建议采用“域驱动失效”思路：",{"type":27,"tag":42,"props":2239,"children":2240},{},[2241,2252,2263],{"type":27,"tag":46,"props":2242,"children":2243},{},[2244,2246],{"type":33,"value":2245},"文章：",{"type":27,"tag":212,"props":2247,"children":2249},{"className":2248},[],[2250],{"type":33,"value":2251},"article:{slug}",{"type":27,"tag":46,"props":2253,"children":2254},{},[2255,2257],{"type":33,"value":2256},"文章列表：",{"type":27,"tag":212,"props":2258,"children":2260},{"className":2259},[],[2261],{"type":33,"value":2262},"article:list",{"type":27,"tag":46,"props":2264,"children":2265},{},[2266,2268],{"type":33,"value":2267},"专题：",{"type":27,"tag":212,"props":2269,"children":2271},{"className":2270},[],[2272],{"type":33,"value":2273},"topic:{name}",{"type":27,"tag":36,"props":2275,"children":2276},{},[2277],{"type":33,"value":2278},"当某篇文章更新：",{"type":27,"tag":42,"props":2280,"children":2281},{},[2282,2292],{"type":27,"tag":46,"props":2283,"children":2284},{},[2285,2287],{"type":33,"value":2286},"必失效：",{"type":27,"tag":212,"props":2288,"children":2290},{"className":2289},[],[2291],{"type":33,"value":2251},{"type":27,"tag":46,"props":2293,"children":2294},{},[2295,2297,2302],{"type":33,"value":2296},"可选失效：",{"type":27,"tag":212,"props":2298,"children":2300},{"className":2299},[],[2301],{"type":33,"value":2262},{"type":33,"value":2303},"（看你是否要求“列表立即出现最新文章”）",{"type":27,"tag":36,"props":2305,"children":2306},{},[2307],{"type":33,"value":2308},"这样你能解释每一次失效，而不是“全站清缓存”。",{"type":27,"tag":699,"props":2310,"children":2311},{},[],{"type":27,"tag":28,"props":2313,"children":2315},{"id":2314},"_7-线上排障isr-最常见的-6-个问题",[2316],{"type":33,"value":2317},"7. 线上排障：ISR 最常见的 6 个问题",{"type":27,"tag":513,"props":2319,"children":2321},{"id":2320},"_71-发布了但页面不更新",[2322],{"type":33,"value":2323},"7.1 “发布了但页面不更新”",{"type":27,"tag":36,"props":2325,"children":2326},{},[2327],{"type":33,"value":2328},"排查顺序：",{"type":27,"tag":42,"props":2330,"children":2331},{},[2332,2337,2342],{"type":27,"tag":46,"props":2333,"children":2334},{},[2335],{"type":33,"value":2336},"webhook 是否触发？是否被鉴权拦截？",{"type":27,"tag":46,"props":2338,"children":2339},{},[2340],{"type":33,"value":2341},"失效的是正确的 path/tag 吗？",{"type":27,"tag":46,"props":2343,"children":2344},{},[2345],{"type":33,"value":2346},"CDN 是否仍在强缓存旧内容？（看响应头、Cache-Status）",{"type":27,"tag":513,"props":2348,"children":2350},{"id":2349},"_72-页面更新了但-seo-抓不到",[2351],{"type":33,"value":2352},"7.2 “页面更新了但 SEO 抓不到”",{"type":27,"tag":42,"props":2354,"children":2355},{},[2356,2361,2366],{"type":27,"tag":46,"props":2357,"children":2358},{},[2359],{"type":33,"value":2360},"sitemap 是否更新",{"type":27,"tag":46,"props":2362,"children":2363},{},[2364],{"type":33,"value":2365},"canonical/robots 是否正确",{"type":27,"tag":46,"props":2367,"children":2368},{},[2369],{"type":33,"value":2370},"是否把重要内容放在客户端渲染",{"type":27,"tag":513,"props":2372,"children":2374},{"id":2373},"_73-回源突然暴增",[2375],{"type":33,"value":2376},"7.3 “回源突然暴增”",{"type":27,"tag":36,"props":2378,"children":2379},{},[2380],{"type":33,"value":2381},"常见原因：",{"type":27,"tag":42,"props":2383,"children":2384},{},[2385,2390,2395],{"type":27,"tag":46,"props":2386,"children":2387},{},[2388],{"type":33,"value":2389},"TTL 设太短",{"type":27,"tag":46,"props":2391,"children":2392},{},[2393],{"type":33,"value":2394},"聚合页频繁失效",{"type":27,"tag":46,"props":2396,"children":2397},{},[2398],{"type":33,"value":2399},"缓存命中率被 query 污染（utm）",{"type":27,"tag":513,"props":2401,"children":2403},{"id":2402},"_74-某些地区更新很慢",[2404],{"type":33,"value":2405},"7.4 “某些地区更新很慢”",{"type":27,"tag":42,"props":2407,"children":2408},{},[2409,2414],{"type":27,"tag":46,"props":2410,"children":2411},{},[2412],{"type":33,"value":2413},"CDN 分区缓存",{"type":27,"tag":46,"props":2415,"children":2416},{},[2417],{"type":33,"value":2418},"边缘节点回源延迟",{"type":27,"tag":513,"props":2420,"children":2422},{"id":2421},"_75-低峰正常高峰崩",[2423],{"type":33,"value":2424},"7.5 “低峰正常，高峰崩”",{"type":27,"tag":42,"props":2426,"children":2427},{},[2428,2433],{"type":27,"tag":46,"props":2429,"children":2430},{},[2431],{"type":33,"value":2432},"再生成并发过高",{"type":27,"tag":46,"props":2434,"children":2435},{},[2436],{"type":33,"value":2437},"上游服务扛不住",{"type":27,"tag":36,"props":2439,"children":2440},{},[2441],{"type":33,"value":2109},{"type":27,"tag":42,"props":2443,"children":2444},{},[2445,2450],{"type":27,"tag":46,"props":2446,"children":2447},{},[2448],{"type":33,"value":2449},"限制再生成并发",{"type":27,"tag":46,"props":2451,"children":2452},{},[2453],{"type":33,"value":2454},"对再生成做队列化",{"type":27,"tag":513,"props":2456,"children":2458},{"id":2457},"_76-用户态数据被缓存",[2459],{"type":33,"value":2460},"7.6 “用户态数据被缓存”",{"type":27,"tag":36,"props":2462,"children":2463},{},[2464],{"type":33,"value":2465},"这是最危险的。",{"type":27,"tag":36,"props":2467,"children":2468},{},[2469],{"type":33,"value":2470},"规则：",{"type":27,"tag":42,"props":2472,"children":2473},{},[2474,2479],{"type":27,"tag":46,"props":2475,"children":2476},{},[2477],{"type":33,"value":2478},"用户态页面不要 ISR",{"type":27,"tag":46,"props":2480,"children":2481},{},[2482],{"type":33,"value":2483},"或把私有信息拆出（客户端请求）",{"type":27,"tag":699,"props":2485,"children":2486},{},[],{"type":27,"tag":28,"props":2488,"children":2490},{"id":2489},"_8-上线检查清单",[2491],{"type":33,"value":2492},"8. 上线检查清单",{"type":27,"tag":42,"props":2494,"children":2496},{"className":2495},[428],[2497,2506,2515,2524,2533,2542],{"type":27,"tag":46,"props":2498,"children":2500},{"className":2499},[433],[2501,2504],{"type":27,"tag":436,"props":2502,"children":2503},{"disabled":438,"type":439},[],{"type":33,"value":2505}," 哪些路由使用 ISR？是否排除用户态页面",{"type":27,"tag":46,"props":2507,"children":2509},{"className":2508},[433],[2510,2513],{"type":27,"tag":436,"props":2511,"children":2512},{"disabled":438,"type":439},[],{"type":33,"value":2514}," 详情页与列表页的 TTL 是否合理",{"type":27,"tag":46,"props":2516,"children":2518},{"className":2517},[433],[2519,2522],{"type":27,"tag":436,"props":2520,"children":2521},{"disabled":438,"type":439},[],{"type":33,"value":2523}," 是否有事件驱动失效（CMS webhook）",{"type":27,"tag":46,"props":2525,"children":2527},{"className":2526},[433],[2528,2531],{"type":27,"tag":436,"props":2529,"children":2530},{"disabled":438,"type":439},[],{"type":33,"value":2532}," CDN cache key 是否会被 query 污染",{"type":27,"tag":46,"props":2534,"children":2536},{"className":2535},[433],[2537,2540],{"type":27,"tag":436,"props":2538,"children":2539},{"disabled":438,"type":439},[],{"type":33,"value":2541}," 是否有命中率/回源/QPS 监控",{"type":27,"tag":46,"props":2543,"children":2545},{"className":2544},[433],[2546,2549],{"type":27,"tag":436,"props":2547,"children":2548},{"disabled":438,"type":439},[],{"type":33,"value":2550}," 再生成失败是否可观测（日志、报警）",{"type":27,"tag":699,"props":2552,"children":2553},{},[],{"type":27,"tag":28,"props":2555,"children":2556},{"id":1736},[2557],{"type":33,"value":1736},{"type":27,"tag":36,"props":2559,"children":2560},{},[2561],{"type":33,"value":2562},"ISR 的正确用法是：",{"type":27,"tag":42,"props":2564,"children":2565},{},[2566,2571,2576],{"type":27,"tag":46,"props":2567,"children":2568},{},[2569],{"type":33,"value":2570},"把它当成“带可控失效的静态缓存系统”",{"type":27,"tag":46,"props":2572,"children":2573},{},[2574],{"type":33,"value":2575},"用规则设计失效，而不是全站清缓存",{"type":27,"tag":46,"props":2577,"children":2578},{},[2579],{"type":33,"value":2580},"用监控保证它在高峰依然稳定",{"title":7,"searchDepth":592,"depth":592,"links":2582},[2583,2584,2585,2586,2590,2594,2595,2596,2604,2605],{"id":1812,"depth":595,"text":1801},{"id":1887,"depth":595,"text":1890},{"id":1939,"depth":595,"text":1942},{"id":1984,"depth":595,"text":1987,"children":2587},[2588,2589],{"id":1990,"depth":592,"text":1993},{"id":2037,"depth":592,"text":2040},{"id":2090,"depth":595,"text":2093,"children":2591},[2592,2593],{"id":2096,"depth":592,"text":2099},{"id":2125,"depth":592,"text":2128},{"id":2174,"depth":595,"text":2177},{"id":2229,"depth":595,"text":2232},{"id":2314,"depth":595,"text":2317,"children":2597},[2598,2599,2600,2601,2602,2603],{"id":2320,"depth":592,"text":2323},{"id":2349,"depth":592,"text":2352},{"id":2373,"depth":592,"text":2376},{"id":2402,"depth":592,"text":2405},{"id":2421,"depth":592,"text":2424},{"id":2457,"depth":592,"text":2460},{"id":2489,"depth":595,"text":2492},{"id":1736,"depth":595,"text":1736},"content:topics:next:isr-practice.md","topics/next/isr-practice.md","topics/next/isr-practice",{"_path":560,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2610,"description":2611,"date":619,"topic":5,"author":11,"tags":2612,"image":2616,"featured":438,"readingTime":2617,"body":2618,"_type":609,"_id":3731,"_source":611,"_file":3732,"_stem":3733,"_extension":614},"Server Actions 完整指南","从工作原理到安全边界与缓存语义，系统讲透 Next.js Server Actions 的编程模型，并给出可直接落地的工程化实践范式。",[13,2613,623,2614,2615,621],"Server Actions","表单","安全","/images/topics/next/server-actions.jpg",20,{"type":24,"children":2619,"toc":3697},[2620,2625,2636,2648,2653,2692,2695,2701,2706,2719,2724,2757,2762,2780,2792,2795,2801,2814,2819,2850,2855,2878,2884,2889,2894,2912,2933,2936,2942,2948,2953,2964,2969,2980,2985,3003,3009,3018,3023,3041,3044,3050,3055,3091,3096,3105,3114,3123,3128,3146,3149,3155,3160,3171,3177,3182,3195,3204,3210,3223,3228,3234,3239,3244,3276,3282,3287,3308,3314,3319,3323,3336,3339,3345,3350,3355,3388,3394,3418,3423,3447,3458,3463,3468,3481,3484,3490,3504,3509,3527,3533,3538,3556,3561,3564,3570,3588,3593,3596,3602,3607,3665,3668,3674,3679],{"type":27,"tag":28,"props":2621,"children":2623},{"id":2622},"server-actions-完整指南",[2624],{"type":33,"value":2610},{"type":27,"tag":36,"props":2626,"children":2627},{},[2628,2630,2635],{"type":33,"value":2629},"Server Actions 是 Next.js（App Router 体系）里最有“范式转移”意味的能力之一：它把“从浏览器提交数据 → 走 API → 服务端写库 → 客户端再拉取数据”这条链路，压缩成",{"type":27,"tag":640,"props":2631,"children":2632},{},[2633],{"type":33,"value":2634},"在 UI 里直接调用一个服务端函数",{"type":33,"value":646},{"type":27,"tag":36,"props":2637,"children":2638},{},[2639,2641,2646],{"type":33,"value":2640},"但它并不是“把后端写进前端”，而是一套围绕 ",{"type":27,"tag":640,"props":2642,"children":2643},{},[2644],{"type":33,"value":2645},"RSC（React Server Components）边界、请求序列化、缓存与失效、安全隔离",{"type":33,"value":2647}," 重新定义的交互协议。",{"type":27,"tag":36,"props":2649,"children":2650},{},[2651],{"type":33,"value":2652},"这篇文章按“能在生产落地”为标准，依次讲清：",{"type":27,"tag":42,"props":2654,"children":2655},{},[2656,2661,2672,2677,2682,2687],{"type":27,"tag":46,"props":2657,"children":2658},{},[2659],{"type":33,"value":2660},"什么时候该用/不该用 Server Actions",{"type":27,"tag":46,"props":2662,"children":2663},{},[2664,2670],{"type":27,"tag":212,"props":2665,"children":2667},{"className":2666},[],[2668],{"type":33,"value":2669},"'use server'",{"type":33,"value":2671}," 的语义与编译期边界",{"type":27,"tag":46,"props":2673,"children":2674},{},[2675],{"type":33,"value":2676},"表单提交、并发与幂等",{"type":27,"tag":46,"props":2678,"children":2679},{},[2680],{"type":33,"value":2681},"权限、校验、CSRF/重放、敏感信息",{"type":27,"tag":46,"props":2683,"children":2684},{},[2685],{"type":33,"value":2686},"缓存与 revalidate 的正确打开方式",{"type":27,"tag":46,"props":2688,"children":2689},{},[2690],{"type":33,"value":2691},"一套可复用的工程化结构（Action 层、schema、错误码）",{"type":27,"tag":699,"props":2693,"children":2694},{},[],{"type":27,"tag":28,"props":2696,"children":2698},{"id":2697},"_1-server-actions-解决的到底是什么问题",[2699],{"type":33,"value":2700},"1. Server Actions 解决的到底是什么问题？",{"type":27,"tag":36,"props":2702,"children":2703},{},[2704],{"type":33,"value":2705},"在传统前端架构里，“写操作”通常需要两套代码：",{"type":27,"tag":238,"props":2707,"children":2708},{},[2709,2714],{"type":27,"tag":46,"props":2710,"children":2711},{},[2712],{"type":33,"value":2713},"UI 层构造请求（fetch/axios）",{"type":27,"tag":46,"props":2715,"children":2716},{},[2717],{"type":33,"value":2718},"API 层解析请求、校验、执行业务",{"type":27,"tag":36,"props":2720,"children":2721},{},[2722],{"type":33,"value":2723},"这导致三个常见痛点：",{"type":27,"tag":42,"props":2725,"children":2726},{},[2727,2737,2747],{"type":27,"tag":46,"props":2728,"children":2729},{},[2730,2735],{"type":27,"tag":640,"props":2731,"children":2732},{},[2733],{"type":33,"value":2734},"样板代码多",{"type":33,"value":2736},"：DTO、路由、controller、错误映射",{"type":27,"tag":46,"props":2738,"children":2739},{},[2740,2745],{"type":27,"tag":640,"props":2741,"children":2742},{},[2743],{"type":33,"value":2744},"类型不一致",{"type":33,"value":2746},"：前后端 schema 漂移，靠约定或手写 types",{"type":27,"tag":46,"props":2748,"children":2749},{},[2750,2755],{"type":27,"tag":640,"props":2751,"children":2752},{},[2753],{"type":33,"value":2754},"缓存/失效复杂",{"type":33,"value":2756},"：写完数据后，页面该怎么更新？哪些缓存该失效？",{"type":27,"tag":36,"props":2758,"children":2759},{},[2760],{"type":33,"value":2761},"Server Actions 的核心价值是：",{"type":27,"tag":42,"props":2763,"children":2764},{},[2765,2775],{"type":27,"tag":46,"props":2766,"children":2767},{},[2768,2773],{"type":27,"tag":640,"props":2769,"children":2770},{},[2771],{"type":33,"value":2772},"把“写操作”收敛成一个服务端函数",{"type":33,"value":2774},"，由框架负责把调用从 client 路由到 server；",{"type":27,"tag":46,"props":2776,"children":2777},{},[2778],{"type":33,"value":2779},"在 App Router 的缓存语义下，提供与页面缓存/请求缓存更一致的“失效”手段。",{"type":27,"tag":36,"props":2781,"children":2782},{},[2783,2785,2790],{"type":33,"value":2784},"你可以把它理解为：Next.js 给“写操作”提供了一个",{"type":27,"tag":640,"props":2786,"children":2787},{},[2788],{"type":33,"value":2789},"与 RSC/缓存模型原生兼容",{"type":33,"value":2791},"的调用入口。",{"type":27,"tag":699,"props":2793,"children":2794},{},[],{"type":27,"tag":28,"props":2796,"children":2798},{"id":2797},"_2-心智模型server-actions-与-rsc-的边界",[2799],{"type":33,"value":2800},"2. 心智模型：Server Actions 与 RSC 的边界",{"type":27,"tag":513,"props":2802,"children":2804},{"id":2803},"_21-use-server-不是运行时开关而是编译期边界",[2805,2807,2812],{"type":33,"value":2806},"2.1 ",{"type":27,"tag":212,"props":2808,"children":2810},{"className":2809},[],[2811],{"type":33,"value":2669},{"type":33,"value":2813}," 不是运行时开关，而是编译期边界",{"type":27,"tag":36,"props":2815,"children":2816},{},[2817],{"type":33,"value":2818},"在 Next.js 中：",{"type":27,"tag":42,"props":2820,"children":2821},{},[2822,2845],{"type":27,"tag":46,"props":2823,"children":2824},{},[2825,2831,2832,2837,2839,2844],{"type":27,"tag":212,"props":2826,"children":2828},{"className":2827},[],[2829],{"type":33,"value":2830},"'use client'",{"type":33,"value":830},{"type":27,"tag":212,"props":2833,"children":2835},{"className":2834},[],[2836],{"type":33,"value":2669},{"type":33,"value":2838}," 是",{"type":27,"tag":640,"props":2840,"children":2841},{},[2842],{"type":33,"value":2843},"模块级/函数级的编译指令",{"type":33,"value":646},{"type":27,"tag":46,"props":2846,"children":2847},{},[2848],{"type":33,"value":2849},"被标记为 Server Action 的函数只能在服务端执行；在客户端调用时，框架会生成一个“可调用的引用”，最终由服务端执行。",{"type":27,"tag":36,"props":2851,"children":2852},{},[2853],{"type":33,"value":2854},"一个直观的理解：",{"type":27,"tag":42,"props":2856,"children":2857},{},[2858,2868],{"type":27,"tag":46,"props":2859,"children":2860},{},[2861,2866],{"type":27,"tag":640,"props":2862,"children":2863},{},[2864],{"type":33,"value":2865},"你写的是函数",{"type":33,"value":2867},"；",{"type":27,"tag":46,"props":2869,"children":2870},{},[2871,2876],{"type":27,"tag":640,"props":2872,"children":2873},{},[2874],{"type":33,"value":2875},"框架生成的是 RPC",{"type":33,"value":2877},"（带序列化、鉴权/上下文、执行、返回）。",{"type":27,"tag":513,"props":2879,"children":2881},{"id":2880},"_22-传参限制可序列化是第一原则",[2882],{"type":33,"value":2883},"2.2 传参限制：可序列化是第一原则",{"type":27,"tag":36,"props":2885,"children":2886},{},[2887],{"type":33,"value":2888},"Server Action 的参数需要能安全序列化。",{"type":27,"tag":36,"props":2890,"children":2891},{},[2892],{"type":33,"value":2893},"建议遵循：",{"type":27,"tag":42,"props":2895,"children":2896},{},[2897,2907],{"type":27,"tag":46,"props":2898,"children":2899},{},[2900,2902],{"type":33,"value":2901},"只传 ",{"type":27,"tag":640,"props":2903,"children":2904},{},[2905],{"type":33,"value":2906},"primitive / plain object / array / FormData",{"type":27,"tag":46,"props":2908,"children":2909},{},[2910],{"type":33,"value":2911},"不传 class 实例、函数、DOM 节点、复杂原型对象",{"type":27,"tag":36,"props":2913,"children":2914},{},[2915,2917,2923,2925,2931],{"type":33,"value":2916},"如果你发现自己想把“整个业务对象”丢进去，通常意味着你应该传一个 ",{"type":27,"tag":212,"props":2918,"children":2920},{"className":2919},[],[2921],{"type":33,"value":2922},"id",{"type":33,"value":2924}," 或 ",{"type":27,"tag":212,"props":2926,"children":2928},{"className":2927},[],[2929],{"type":33,"value":2930},"payload",{"type":33,"value":2932},"，然后在服务端再查询/校验。",{"type":27,"tag":699,"props":2934,"children":2935},{},[],{"type":27,"tag":28,"props":2937,"children":2939},{"id":2938},"_3-两种主流用法表单-action-与事件-action",[2940],{"type":33,"value":2941},"3. 两种主流用法：表单 Action 与事件 Action",{"type":27,"tag":513,"props":2943,"children":2945},{"id":2944},"_31-表单提交推荐天然-csrf-友好契合-web-标准",[2946],{"type":33,"value":2947},"3.1 表单提交（推荐）：天然 CSRF 友好，契合 Web 标准",{"type":27,"tag":36,"props":2949,"children":2950},{},[2951],{"type":33,"value":2952},"Server Actions 最推荐的入口是表单：",{"type":27,"tag":204,"props":2954,"children":2959},{"className":2955,"code":2957,"language":2958,"meta":7},[2956],"language-tsx","// app/settings/page.tsx\nimport { updateProfileAction } from './actions'\n\nexport default function SettingsPage() {\n  return (\n    \u003Cform action={updateProfileAction}>\n      \u003Cinput name=\"displayName\" />\n      \u003Cbutton type=\"submit\">保存\u003C/button>\n    \u003C/form>\n  )\n}\n","tsx",[2960],{"type":27,"tag":212,"props":2961,"children":2962},{"__ignoreMap":7},[2963],{"type":33,"value":2957},{"type":27,"tag":36,"props":2965,"children":2966},{},[2967],{"type":33,"value":2968},"在服务端：",{"type":27,"tag":204,"props":2970,"children":2975},{"className":2971,"code":2973,"language":2974,"meta":7},[2972],"language-ts","// app/settings/actions.ts\n'use server'\n\nexport async function updateProfileAction(formData: FormData) {\n  const displayName = String(formData.get('displayName') || '').trim()\n  // 校验、鉴权、写库...\n}\n","ts",[2976],{"type":27,"tag":212,"props":2977,"children":2978},{"__ignoreMap":7},[2979],{"type":33,"value":2973},{"type":27,"tag":36,"props":2981,"children":2982},{},[2983],{"type":33,"value":2984},"为什么推荐表单？",{"type":27,"tag":42,"props":2986,"children":2987},{},[2988,2993,2998],{"type":27,"tag":46,"props":2989,"children":2990},{},[2991],{"type":33,"value":2992},"浏览器原生语义：回退/刷新更自然",{"type":27,"tag":46,"props":2994,"children":2995},{},[2996],{"type":33,"value":2997},"你更容易把“输入 → 校验 → 保存”做成明确的流水线",{"type":27,"tag":46,"props":2999,"children":3000},{},[3001],{"type":33,"value":3002},"比“按钮 onClick 调 action”更不容易写出隐式并发 bug",{"type":27,"tag":513,"props":3004,"children":3006},{"id":3005},"_32-事件触发慎用更像-rpc必须处理并发与幂等",[3007],{"type":33,"value":3008},"3.2 事件触发（慎用）：更像 RPC，必须处理并发与幂等",{"type":27,"tag":204,"props":3010,"children":3013},{"className":3011,"code":3012,"language":2958,"meta":7},[2956],"'use client'\nimport { toggleStarAction } from './actions'\n\nexport function StarButton({ id }: { id: string }) {\n  return (\n    \u003Cbutton\n      onClick={async () => {\n        await toggleStarAction(id)\n      }}\n    >\n      Star\n    \u003C/button>\n  )\n}\n",[3014],{"type":27,"tag":212,"props":3015,"children":3016},{"__ignoreMap":7},[3017],{"type":33,"value":3012},{"type":27,"tag":36,"props":3019,"children":3020},{},[3021],{"type":33,"value":3022},"这类用法更像“客户端发起 RPC”，你需要格外注意：",{"type":27,"tag":42,"props":3024,"children":3025},{},[3026,3031,3036],{"type":27,"tag":46,"props":3027,"children":3028},{},[3029],{"type":33,"value":3030},"重复点击导致并发",{"type":27,"tag":46,"props":3032,"children":3033},{},[3034],{"type":33,"value":3035},"网络抖动导致重试",{"type":27,"tag":46,"props":3037,"children":3038},{},[3039],{"type":33,"value":3040},"乐观更新与最终一致",{"type":27,"tag":699,"props":3042,"children":3043},{},[],{"type":27,"tag":28,"props":3045,"children":3047},{"id":3046},"_4-工程化落地action-层应该长什么样",[3048],{"type":33,"value":3049},"4. 工程化落地：Action 层应该长什么样？",{"type":27,"tag":36,"props":3051,"children":3052},{},[3053],{"type":33,"value":3054},"一个能长期维护的结构，通常把“Action 的对外接口”与“业务实现”分离：",{"type":27,"tag":42,"props":3056,"children":3057},{},[3058,3069,3080],{"type":27,"tag":46,"props":3059,"children":3060},{},[3061,3067],{"type":27,"tag":212,"props":3062,"children":3064},{"className":3063},[],[3065],{"type":33,"value":3066},"actions/*.ts",{"type":33,"value":3068},"：只负责输入解析、校验、鉴权、错误映射、触发失效",{"type":27,"tag":46,"props":3070,"children":3071},{},[3072,3078],{"type":27,"tag":212,"props":3073,"children":3075},{"className":3074},[],[3076],{"type":33,"value":3077},"services/*.ts",{"type":33,"value":3079},"：纯业务逻辑（可被 API/任务队列复用）",{"type":27,"tag":46,"props":3081,"children":3082},{},[3083,3089],{"type":27,"tag":212,"props":3084,"children":3086},{"className":3085},[],[3087],{"type":33,"value":3088},"schemas/*.ts",{"type":33,"value":3090},"：输入 schema（建议用 zod 或你们统一的校验工具）",{"type":27,"tag":36,"props":3092,"children":3093},{},[3094],{"type":33,"value":3095},"示例：",{"type":27,"tag":204,"props":3097,"children":3100},{"className":3098,"code":3099,"language":2974,"meta":7},[2972],"// app/settings/schemas.ts\nimport { z } from 'zod'\n\nexport const updateProfileSchema = z.object({\n  displayName: z.string().min(2).max(32),\n})\n",[3101],{"type":27,"tag":212,"props":3102,"children":3103},{"__ignoreMap":7},[3104],{"type":33,"value":3099},{"type":27,"tag":204,"props":3106,"children":3109},{"className":3107,"code":3108,"language":2974,"meta":7},[2972],"// app/settings/services.ts\nexport async function updateProfile(userId: string, input: { displayName: string }) {\n  // 写库、写缓存、发事件...\n}\n",[3110],{"type":27,"tag":212,"props":3111,"children":3112},{"__ignoreMap":7},[3113],{"type":33,"value":3108},{"type":27,"tag":204,"props":3115,"children":3118},{"className":3116,"code":3117,"language":2974,"meta":7},[2972],"// app/settings/actions.ts\n'use server'\n\nimport { updateProfileSchema } from './schemas'\nimport { updateProfile } from './services'\nimport { revalidatePath } from 'next/cache'\n\nclass ActionError extends Error {\n  code: string\n  constructor(code: string, message: string) {\n    super(message)\n    this.code = code\n  }\n}\n\nfunction requireUserId(): string {\n  // 伪代码：从会话中拿 user\n  // const user = await auth()\n  // if (!user) throw new ActionError('UNAUTHORIZED', '请先登录')\n  return 'user_123'\n}\n\nexport async function updateProfileAction(formData: FormData) {\n  const userId = requireUserId()\n\n  const input = {\n    displayName: String(formData.get('displayName') || '').trim(),\n  }\n\n  const parsed = updateProfileSchema.safeParse(input)\n  if (!parsed.success) {\n    throw new ActionError('VALIDATION_ERROR', '输入不合法')\n  }\n\n  await updateProfile(userId, parsed.data)\n\n  // 关键：写操作后做缓存失效\n  revalidatePath('/settings')\n}\n",[3119],{"type":27,"tag":212,"props":3120,"children":3121},{"__ignoreMap":7},[3122],{"type":33,"value":3117},{"type":27,"tag":36,"props":3124,"children":3125},{},[3126],{"type":33,"value":3127},"这个结构的好处：",{"type":27,"tag":42,"props":3129,"children":3130},{},[3131,3136,3141],{"type":27,"tag":46,"props":3132,"children":3133},{},[3134],{"type":33,"value":3135},"Action 层是“协议适配器”，服务层是“领域逻辑”",{"type":27,"tag":46,"props":3137,"children":3138},{},[3139],{"type":33,"value":3140},"输入校验与鉴权在入口处统一完成",{"type":27,"tag":46,"props":3142,"children":3143},{},[3144],{"type":33,"value":3145},"缓存失效在入口处统一声明",{"type":27,"tag":699,"props":3147,"children":3148},{},[],{"type":27,"tag":28,"props":3150,"children":3152},{"id":3151},"_5-安全边界你必须明确回答的-5-个问题",[3153],{"type":33,"value":3154},"5. 安全边界：你必须明确回答的 5 个问题",{"type":27,"tag":36,"props":3156,"children":3157},{},[3158],{"type":33,"value":3159},"Server Actions 容易让人产生错觉：“既然在 server 执行，那就是安全的”。",{"type":27,"tag":36,"props":3161,"children":3162},{},[3163,3165,3170],{"type":33,"value":3164},"更准确的说法是：",{"type":27,"tag":640,"props":3166,"children":3167},{},[3168],{"type":33,"value":3169},"Server Actions 让调用路径更短，但安全问题一个都不会消失",{"type":33,"value":646},{"type":27,"tag":513,"props":3172,"children":3174},{"id":3173},"_51-认证action-里必须做鉴权",[3175],{"type":33,"value":3176},"5.1 认证：Action 里必须做鉴权",{"type":27,"tag":36,"props":3178,"children":3179},{},[3180],{"type":33,"value":3181},"不要假设“只有页面能调用它”。",{"type":27,"tag":42,"props":3183,"children":3184},{},[3185,3190],{"type":27,"tag":46,"props":3186,"children":3187},{},[3188],{"type":33,"value":3189},"攻击者可以构造请求直接触发 action",{"type":27,"tag":46,"props":3191,"children":3192},{},[3193],{"type":33,"value":3194},"也可能通过 XSS / 依赖污染从客户端触发",{"type":27,"tag":36,"props":3196,"children":3197},{},[3198,3199],{"type":33,"value":960},{"type":27,"tag":640,"props":3200,"children":3201},{},[3202],{"type":33,"value":3203},"每个会修改数据的 action，都要在服务端检查身份与权限。",{"type":27,"tag":513,"props":3205,"children":3207},{"id":3206},"_52-输入校验永远不要信任-formdata",[3208],{"type":33,"value":3209},"5.2 输入校验：永远不要信任 FormData",{"type":27,"tag":42,"props":3211,"children":3212},{},[3213,3218],{"type":27,"tag":46,"props":3214,"children":3215},{},[3216],{"type":33,"value":3217},"字段缺失、空字符串、超长、类型错",{"type":27,"tag":46,"props":3219,"children":3220},{},[3221],{"type":33,"value":3222},"业务约束（例如“昵称不得包含敏感词”）",{"type":27,"tag":36,"props":3224,"children":3225},{},[3226],{"type":33,"value":3227},"建议：用 schema 校验（zod）或你们统一的 validator。",{"type":27,"tag":513,"props":3229,"children":3231},{"id":3230},"_53-幂等与重放写操作要能承受重复执行",[3232],{"type":33,"value":3233},"5.3 幂等与重放：写操作要能承受重复执行",{"type":27,"tag":36,"props":3235,"children":3236},{},[3237],{"type":33,"value":3238},"浏览器/网络层可能导致重复提交。",{"type":27,"tag":36,"props":3240,"children":3241},{},[3242],{"type":33,"value":3243},"常见策略：",{"type":27,"tag":42,"props":3245,"children":3246},{},[3247,3258,3271],{"type":27,"tag":46,"props":3248,"children":3249},{},[3250,3252],{"type":33,"value":3251},"业务幂等键：",{"type":27,"tag":212,"props":3253,"children":3255},{"className":3254},[],[3256],{"type":33,"value":3257},"idempotencyKey",{"type":27,"tag":46,"props":3259,"children":3260},{},[3261,3263,3269],{"type":33,"value":3262},"数据层唯一约束：例如 ",{"type":27,"tag":212,"props":3264,"children":3266},{"className":3265},[],[3267],{"type":33,"value":3268},"(userId, itemId)",{"type":33,"value":3270}," 唯一",{"type":27,"tag":46,"props":3272,"children":3273},{},[3274],{"type":33,"value":3275},"事务：确保写入一致",{"type":27,"tag":513,"props":3277,"children":3279},{"id":3278},"_54-csrf优先走-form-action并启用-same-site-策略",[3280],{"type":33,"value":3281},"5.4 CSRF：优先走 form action，并启用 same-site 策略",{"type":27,"tag":36,"props":3283,"children":3284},{},[3285],{"type":33,"value":3286},"Server Actions 不等于“自动免疫 CSRF”。",{"type":27,"tag":42,"props":3288,"children":3289},{},[3290,3295],{"type":27,"tag":46,"props":3291,"children":3292},{},[3293],{"type":33,"value":3294},"如果你依赖 cookie 会话，仍要考虑跨站请求",{"type":27,"tag":46,"props":3296,"children":3297},{},[3298,3300,3306],{"type":33,"value":3299},"form action 的默认行为更符合浏览器标准，但你仍需要正确配置 cookie 的 ",{"type":27,"tag":212,"props":3301,"children":3303},{"className":3302},[],[3304],{"type":33,"value":3305},"SameSite",{"type":33,"value":3307}," 和 token",{"type":27,"tag":513,"props":3309,"children":3311},{"id":3310},"_55-错误回传不要把内部错误直接暴露给用户",[3312],{"type":33,"value":3313},"5.5 错误回传：不要把内部错误直接暴露给用户",{"type":27,"tag":36,"props":3315,"children":3316},{},[3317],{"type":33,"value":3318},"Action 抛出的错误最终会影响 UI。",{"type":27,"tag":36,"props":3320,"children":3321},{},[3322],{"type":33,"value":1097},{"type":27,"tag":42,"props":3324,"children":3325},{},[3326,3331],{"type":27,"tag":46,"props":3327,"children":3328},{},[3329],{"type":33,"value":3330},"对用户展示：可理解的、稳定的错误码/文案",{"type":27,"tag":46,"props":3332,"children":3333},{},[3334],{"type":33,"value":3335},"对服务端记录：完整 stack、请求上下文、userId、traceId",{"type":27,"tag":699,"props":3337,"children":3338},{},[],{"type":27,"tag":28,"props":3340,"children":3342},{"id":3341},"_6-缓存语义写操作之后页面为什么不更新",[3343],{"type":33,"value":3344},"6. 缓存语义：写操作之后，页面为什么不更新？",{"type":27,"tag":36,"props":3346,"children":3347},{},[3348],{"type":33,"value":3349},"在 App Router 模型里，“看起来像 SSR 的页面”实际上可能被缓存。",{"type":27,"tag":36,"props":3351,"children":3352},{},[3353],{"type":33,"value":3354},"你需要掌握 3 个概念：",{"type":27,"tag":42,"props":3356,"children":3357},{},[3358,3368,3378],{"type":27,"tag":46,"props":3359,"children":3360},{},[3361,3366],{"type":27,"tag":640,"props":3362,"children":3363},{},[3364],{"type":33,"value":3365},"请求缓存",{"type":33,"value":3367},"（fetch cache）",{"type":27,"tag":46,"props":3369,"children":3370},{},[3371,3376],{"type":27,"tag":640,"props":3372,"children":3373},{},[3374],{"type":33,"value":3375},"页面/路由段缓存",{"type":33,"value":3377},"（RSC payload cache）",{"type":27,"tag":46,"props":3379,"children":3380},{},[3381,3386],{"type":27,"tag":640,"props":3382,"children":3383},{},[3384],{"type":33,"value":3385},"失效机制",{"type":33,"value":3387},"（revalidatePath / revalidateTag）",{"type":27,"tag":513,"props":3389,"children":3391},{"id":3390},"_61-写操作后推荐怎么做",[3392],{"type":33,"value":3393},"6.1 写操作后，推荐怎么做？",{"type":27,"tag":42,"props":3395,"children":3396},{},[3397,3407],{"type":27,"tag":46,"props":3398,"children":3399},{},[3400,3402],{"type":33,"value":3401},"更新某个页面：",{"type":27,"tag":212,"props":3403,"children":3405},{"className":3404},[],[3406],{"type":33,"value":1213},{"type":27,"tag":46,"props":3408,"children":3409},{},[3410,3412],{"type":33,"value":3411},"更新多个数据源：给 fetch 打 tag，然后 ",{"type":27,"tag":212,"props":3413,"children":3415},{"className":3414},[],[3416],{"type":33,"value":3417},"revalidateTag('tag-name')",{"type":27,"tag":36,"props":3419,"children":3420},{},[3421],{"type":33,"value":3422},"建议的经验法则：",{"type":27,"tag":42,"props":3424,"children":3425},{},[3426,3437],{"type":27,"tag":46,"props":3427,"children":3428},{},[3429,3431],{"type":33,"value":3430},"页面级别更新不多：优先 ",{"type":27,"tag":212,"props":3432,"children":3434},{"className":3433},[],[3435],{"type":33,"value":3436},"revalidatePath",{"type":27,"tag":46,"props":3438,"children":3439},{},[3440,3442],{"type":33,"value":3441},"数据在多个页面复用：优先 ",{"type":27,"tag":212,"props":3443,"children":3445},{"className":3444},[],[3446],{"type":33,"value":624},{"type":27,"tag":513,"props":3448,"children":3450},{"id":3449},"_62-不要滥用-revalidatepath",[3451,3453],{"type":33,"value":3452},"6.2 不要滥用 ",{"type":27,"tag":212,"props":3454,"children":3456},{"className":3455},[],[3457],{"type":33,"value":1367},{"type":27,"tag":36,"props":3459,"children":3460},{},[3461],{"type":33,"value":3462},"这会让你的缓存命中率断崖式下降。",{"type":27,"tag":36,"props":3464,"children":3465},{},[3466],{"type":33,"value":3467},"更好的做法：",{"type":27,"tag":42,"props":3469,"children":3470},{},[3471,3476],{"type":27,"tag":46,"props":3472,"children":3473},{},[3474],{"type":33,"value":3475},"只失效受影响的 route segment",{"type":27,"tag":46,"props":3477,"children":3478},{},[3479],{"type":33,"value":3480},"用 tag 精准失效",{"type":27,"tag":699,"props":3482,"children":3483},{},[],{"type":27,"tag":28,"props":3485,"children":3487},{"id":3486},"_7-并发与用户体验让-action-看起来快而可靠",[3488],{"type":33,"value":3489},"7. 并发与用户体验：让 Action 看起来“快而可靠”",{"type":27,"tag":513,"props":3491,"children":3493},{"id":3492},"_71-usetransition-与-pending-状态",[3494,3496,3502],{"type":33,"value":3495},"7.1 ",{"type":27,"tag":212,"props":3497,"children":3499},{"className":3498},[],[3500],{"type":33,"value":3501},"useTransition",{"type":33,"value":3503}," 与 Pending 状态",{"type":27,"tag":36,"props":3505,"children":3506},{},[3507],{"type":33,"value":3508},"当 action 触发后，用户需要看到明确反馈。",{"type":27,"tag":42,"props":3510,"children":3511},{},[3512,3517,3522],{"type":27,"tag":46,"props":3513,"children":3514},{},[3515],{"type":33,"value":3516},"按钮 loading",{"type":27,"tag":46,"props":3518,"children":3519},{},[3520],{"type":33,"value":3521},"禁用重复提交",{"type":27,"tag":46,"props":3523,"children":3524},{},[3525],{"type":33,"value":3526},"成功/失败 toast",{"type":27,"tag":513,"props":3528,"children":3530},{"id":3529},"_72-乐观更新谨慎",[3531],{"type":33,"value":3532},"7.2 乐观更新（谨慎）",{"type":27,"tag":36,"props":3534,"children":3535},{},[3536],{"type":33,"value":3537},"乐观更新适合：",{"type":27,"tag":42,"props":3539,"children":3540},{},[3541,3546,3551],{"type":27,"tag":46,"props":3542,"children":3543},{},[3544],{"type":33,"value":3545},"可快速回滚",{"type":27,"tag":46,"props":3547,"children":3548},{},[3549],{"type":33,"value":3550},"失败概率低",{"type":27,"tag":46,"props":3552,"children":3553},{},[3554],{"type":33,"value":3555},"用户对即时反馈敏感（点赞/收藏）",{"type":27,"tag":36,"props":3557,"children":3558},{},[3559],{"type":33,"value":3560},"对“资金/权限/关键数据”写操作不建议乐观更新。",{"type":27,"tag":699,"props":3562,"children":3563},{},[],{"type":27,"tag":28,"props":3565,"children":3567},{"id":3566},"_8-什么时候不该用-server-actions",[3568],{"type":33,"value":3569},"8. 什么时候不该用 Server Actions？",{"type":27,"tag":42,"props":3571,"children":3572},{},[3573,3578,3583],{"type":27,"tag":46,"props":3574,"children":3575},{},[3576],{"type":33,"value":3577},"需要对外开放的公共 API（给第三方/移动端）",{"type":27,"tag":46,"props":3579,"children":3580},{},[3581],{"type":33,"value":3582},"需要长时间运行的任务（应改为队列/异步任务）",{"type":27,"tag":46,"props":3584,"children":3585},{},[3586],{"type":33,"value":3587},"需要复杂流控/网关策略（限流、WAF、跨服务认证）",{"type":27,"tag":36,"props":3589,"children":3590},{},[3591],{"type":33,"value":3592},"Server Actions 是“Web 应用内的写操作入口”，不是 API 网关替代品。",{"type":27,"tag":699,"props":3594,"children":3595},{},[],{"type":27,"tag":28,"props":3597,"children":3599},{"id":3598},"_9-生产级检查清单",[3600],{"type":33,"value":3601},"9. 生产级检查清单",{"type":27,"tag":36,"props":3603,"children":3604},{},[3605],{"type":33,"value":3606},"在把某个写操作迁移到 Server Actions 前，按这份清单过一遍：",{"type":27,"tag":42,"props":3608,"children":3610},{"className":3609},[428],[3611,3620,3629,3638,3647,3656],{"type":27,"tag":46,"props":3612,"children":3614},{"className":3613},[433],[3615,3618],{"type":27,"tag":436,"props":3616,"children":3617},{"disabled":438,"type":439},[],{"type":33,"value":3619}," action 是否做了鉴权与权限校验",{"type":27,"tag":46,"props":3621,"children":3623},{"className":3622},[433],[3624,3627],{"type":27,"tag":436,"props":3625,"children":3626},{"disabled":438,"type":439},[],{"type":33,"value":3628}," 输入是否做了 schema 校验",{"type":27,"tag":46,"props":3630,"children":3632},{"className":3631},[433],[3633,3636],{"type":27,"tag":436,"props":3634,"children":3635},{"disabled":438,"type":439},[],{"type":33,"value":3637}," 是否具备幂等策略（或数据层唯一约束）",{"type":27,"tag":46,"props":3639,"children":3641},{"className":3640},[433],[3642,3645],{"type":27,"tag":436,"props":3643,"children":3644},{"disabled":438,"type":439},[],{"type":33,"value":3646}," 是否定义了稳定的错误码/文案",{"type":27,"tag":46,"props":3648,"children":3650},{"className":3649},[433],[3651,3654],{"type":27,"tag":436,"props":3652,"children":3653},{"disabled":438,"type":439},[],{"type":33,"value":3655}," 写操作后是否做了精确的缓存失效（path/tag）",{"type":27,"tag":46,"props":3657,"children":3659},{"className":3658},[433],[3660,3663],{"type":27,"tag":436,"props":3661,"children":3662},{"disabled":438,"type":439},[],{"type":33,"value":3664}," 是否有可观测性：日志、traceId、错误上报",{"type":27,"tag":699,"props":3666,"children":3667},{},[],{"type":27,"tag":28,"props":3669,"children":3671},{"id":3670},"_10-总结",[3672],{"type":33,"value":3673},"10. 总结",{"type":27,"tag":36,"props":3675,"children":3676},{},[3677],{"type":33,"value":3678},"Server Actions 的正确价值不是“少写 API”，而是：",{"type":27,"tag":42,"props":3680,"children":3681},{},[3682,3687,3692],{"type":27,"tag":46,"props":3683,"children":3684},{},[3685],{"type":33,"value":3686},"把写操作纳入 App Router/RSC 的统一模型",{"type":27,"tag":46,"props":3688,"children":3689},{},[3690],{"type":33,"value":3691},"让缓存失效与 UI 刷新更一致",{"type":27,"tag":46,"props":3693,"children":3694},{},[3695],{"type":33,"value":3696},"在正确工程结构下，显著减少样板代码与类型漂移",{"title":7,"searchDepth":592,"depth":592,"links":3698},[3699,3700,3701,3706,3710,3711,3718,3723,3728,3729,3730],{"id":2622,"depth":595,"text":2610},{"id":2697,"depth":595,"text":2700},{"id":2797,"depth":595,"text":2800,"children":3702},[3703,3705],{"id":2803,"depth":592,"text":3704},"2.1 'use server' 不是运行时开关，而是编译期边界",{"id":2880,"depth":592,"text":2883},{"id":2938,"depth":595,"text":2941,"children":3707},[3708,3709],{"id":2944,"depth":592,"text":2947},{"id":3005,"depth":592,"text":3008},{"id":3046,"depth":595,"text":3049},{"id":3151,"depth":595,"text":3154,"children":3712},[3713,3714,3715,3716,3717],{"id":3173,"depth":592,"text":3176},{"id":3206,"depth":592,"text":3209},{"id":3230,"depth":592,"text":3233},{"id":3278,"depth":592,"text":3281},{"id":3310,"depth":592,"text":3313},{"id":3341,"depth":595,"text":3344,"children":3719},[3720,3721],{"id":3390,"depth":592,"text":3393},{"id":3449,"depth":592,"text":3722},"6.2 不要滥用 revalidatePath('/')",{"id":3486,"depth":595,"text":3489,"children":3724},[3725,3727],{"id":3492,"depth":592,"text":3726},"7.1 useTransition 与 Pending 状态",{"id":3529,"depth":592,"text":3532},{"id":3566,"depth":595,"text":3569},{"id":3598,"depth":595,"text":3601},{"id":3670,"depth":595,"text":3673},"content:topics:next:server-actions-complete-guide.md","topics/next/server-actions-complete-guide.md","topics/next/server-actions-complete-guide",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":3735,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":3736,"_type":609,"_id":610,"_source":611,"_file":612,"_stem":613,"_extension":614},[13,14,15,16,17],{"type":24,"children":3737,"toc":4178},[3738,3742,3746,3765,3769,3773,3777,3861,3865,3869,3873,3881,3885,3889,3893,3897,3912,3916,3920,3924,3943,3947,3951,3966,3970,3974,3993,3997,4001,4005,4020,4024,4043,4047,4115,4119,4123,4127,4131,4135,4139,4143,4147],{"type":27,"tag":28,"props":3739,"children":3740},{"id":30},[3741],{"type":33,"value":34},{"type":27,"tag":36,"props":3743,"children":3744},{},[3745],{"type":33,"value":40},{"type":27,"tag":42,"props":3747,"children":3748},{},[3749,3753,3757,3761],{"type":27,"tag":46,"props":3750,"children":3751},{},[3752],{"type":33,"value":50},{"type":27,"tag":46,"props":3754,"children":3755},{},[3756],{"type":33,"value":55},{"type":27,"tag":46,"props":3758,"children":3759},{},[3760],{"type":33,"value":60},{"type":27,"tag":46,"props":3762,"children":3763},{},[3764],{"type":33,"value":65},{"type":27,"tag":36,"props":3766,"children":3767},{},[3768],{"type":33,"value":70},{"type":27,"tag":28,"props":3770,"children":3771},{"id":73},[3772],{"type":33,"value":76},{"type":27,"tag":36,"props":3774,"children":3775},{},[3776],{"type":33,"value":81},{"type":27,"tag":83,"props":3778,"children":3779},{},[3780,3798],{"type":27,"tag":87,"props":3781,"children":3782},{},[3783],{"type":27,"tag":91,"props":3784,"children":3785},{},[3786,3790,3794],{"type":27,"tag":95,"props":3787,"children":3788},{},[3789],{"type":33,"value":99},{"type":27,"tag":95,"props":3791,"children":3792},{},[3793],{"type":33,"value":104},{"type":27,"tag":95,"props":3795,"children":3796},{},[3797],{"type":33,"value":109},{"type":27,"tag":111,"props":3799,"children":3800},{},[3801,3816,3831,3846],{"type":27,"tag":91,"props":3802,"children":3803},{},[3804,3808,3812],{"type":27,"tag":118,"props":3805,"children":3806},{},[3807],{"type":33,"value":122},{"type":27,"tag":118,"props":3809,"children":3810},{},[3811],{"type":33,"value":127},{"type":27,"tag":118,"props":3813,"children":3814},{},[3815],{"type":33,"value":132},{"type":27,"tag":91,"props":3817,"children":3818},{},[3819,3823,3827],{"type":27,"tag":118,"props":3820,"children":3821},{},[3822],{"type":33,"value":140},{"type":27,"tag":118,"props":3824,"children":3825},{},[3826],{"type":33,"value":145},{"type":27,"tag":118,"props":3828,"children":3829},{},[3830],{"type":33,"value":150},{"type":27,"tag":91,"props":3832,"children":3833},{},[3834,3838,3842],{"type":27,"tag":118,"props":3835,"children":3836},{},[3837],{"type":33,"value":158},{"type":27,"tag":118,"props":3839,"children":3840},{},[3841],{"type":33,"value":163},{"type":27,"tag":118,"props":3843,"children":3844},{},[3845],{"type":33,"value":168},{"type":27,"tag":91,"props":3847,"children":3848},{},[3849,3853,3857],{"type":27,"tag":118,"props":3850,"children":3851},{},[3852],{"type":33,"value":176},{"type":27,"tag":118,"props":3854,"children":3855},{},[3856],{"type":33,"value":181},{"type":27,"tag":118,"props":3858,"children":3859},{},[3860],{"type":33,"value":186},{"type":27,"tag":36,"props":3862,"children":3863},{},[3864],{"type":33,"value":191},{"type":27,"tag":28,"props":3866,"children":3867},{"id":194},[3868],{"type":33,"value":197},{"type":27,"tag":36,"props":3870,"children":3871},{},[3872],{"type":33,"value":202},{"type":27,"tag":204,"props":3874,"children":3876},{"className":3875,"code":208,"language":209,"meta":7},[207],[3877],{"type":27,"tag":212,"props":3878,"children":3879},{"__ignoreMap":7},[3880],{"type":33,"value":208},{"type":27,"tag":36,"props":3882,"children":3883},{},[3884],{"type":33,"value":220},{"type":27,"tag":28,"props":3886,"children":3887},{"id":223},[3888],{"type":33,"value":226},{"type":27,"tag":36,"props":3890,"children":3891},{},[3892],{"type":33,"value":231},{"type":27,"tag":36,"props":3894,"children":3895},{},[3896],{"type":33,"value":236},{"type":27,"tag":238,"props":3898,"children":3899},{},[3900,3904,3908],{"type":27,"tag":46,"props":3901,"children":3902},{},[3903],{"type":33,"value":245},{"type":27,"tag":46,"props":3905,"children":3906},{},[3907],{"type":33,"value":250},{"type":27,"tag":46,"props":3909,"children":3910},{},[3911],{"type":33,"value":255},{"type":27,"tag":36,"props":3913,"children":3914},{},[3915],{"type":33,"value":260},{"type":27,"tag":28,"props":3917,"children":3918},{"id":263},[3919],{"type":33,"value":266},{"type":27,"tag":36,"props":3921,"children":3922},{},[3923],{"type":33,"value":271},{"type":27,"tag":238,"props":3925,"children":3926},{},[3927,3931,3935,3939],{"type":27,"tag":46,"props":3928,"children":3929},{},[3930],{"type":33,"value":279},{"type":27,"tag":46,"props":3932,"children":3933},{},[3934],{"type":33,"value":284},{"type":27,"tag":46,"props":3936,"children":3937},{},[3938],{"type":33,"value":289},{"type":27,"tag":46,"props":3940,"children":3941},{},[3942],{"type":33,"value":294},{"type":27,"tag":36,"props":3944,"children":3945},{},[3946],{"type":33,"value":299},{"type":27,"tag":36,"props":3948,"children":3949},{},[3950],{"type":33,"value":304},{"type":27,"tag":238,"props":3952,"children":3953},{},[3954,3958,3962],{"type":27,"tag":46,"props":3955,"children":3956},{},[3957],{"type":33,"value":312},{"type":27,"tag":46,"props":3959,"children":3960},{},[3961],{"type":33,"value":317},{"type":27,"tag":46,"props":3963,"children":3964},{},[3965],{"type":33,"value":322},{"type":27,"tag":28,"props":3967,"children":3968},{"id":325},[3969],{"type":33,"value":328},{"type":27,"tag":36,"props":3971,"children":3972},{},[3973],{"type":33,"value":333},{"type":27,"tag":42,"props":3975,"children":3976},{},[3977,3981,3985,3989],{"type":27,"tag":46,"props":3978,"children":3979},{},[3980],{"type":33,"value":341},{"type":27,"tag":46,"props":3982,"children":3983},{},[3984],{"type":33,"value":346},{"type":27,"tag":46,"props":3986,"children":3987},{},[3988],{"type":33,"value":351},{"type":27,"tag":46,"props":3990,"children":3991},{},[3992],{"type":33,"value":356},{"type":27,"tag":36,"props":3994,"children":3995},{},[3996],{"type":33,"value":361},{"type":27,"tag":28,"props":3998,"children":3999},{"id":364},[4000],{"type":33,"value":367},{"type":27,"tag":36,"props":4002,"children":4003},{},[4004],{"type":33,"value":372},{"type":27,"tag":42,"props":4006,"children":4007},{},[4008,4012,4016],{"type":27,"tag":46,"props":4009,"children":4010},{},[4011],{"type":33,"value":380},{"type":27,"tag":46,"props":4013,"children":4014},{},[4015],{"type":33,"value":385},{"type":27,"tag":46,"props":4017,"children":4018},{},[4019],{"type":33,"value":390},{"type":27,"tag":36,"props":4021,"children":4022},{},[4023],{"type":33,"value":395},{"type":27,"tag":42,"props":4025,"children":4026},{},[4027,4031,4035,4039],{"type":27,"tag":46,"props":4028,"children":4029},{},[4030],{"type":33,"value":403},{"type":27,"tag":46,"props":4032,"children":4033},{},[4034],{"type":33,"value":408},{"type":27,"tag":46,"props":4036,"children":4037},{},[4038],{"type":33,"value":413},{"type":27,"tag":46,"props":4040,"children":4041},{},[4042],{"type":33,"value":418},{"type":27,"tag":28,"props":4044,"children":4045},{"id":421},[4046],{"type":33,"value":424},{"type":27,"tag":42,"props":4048,"children":4050},{"className":4049},[428],[4051,4059,4067,4075,4083,4091,4099,4107],{"type":27,"tag":46,"props":4052,"children":4054},{"className":4053},[433],[4055,4058],{"type":27,"tag":436,"props":4056,"children":4057},{"disabled":438,"type":439},[],{"type":33,"value":442},{"type":27,"tag":46,"props":4060,"children":4062},{"className":4061},[433],[4063,4066],{"type":27,"tag":436,"props":4064,"children":4065},{"disabled":438,"type":439},[],{"type":33,"value":451},{"type":27,"tag":46,"props":4068,"children":4070},{"className":4069},[433],[4071,4074],{"type":27,"tag":436,"props":4072,"children":4073},{"disabled":438,"type":439},[],{"type":33,"value":460},{"type":27,"tag":46,"props":4076,"children":4078},{"className":4077},[433],[4079,4082],{"type":27,"tag":436,"props":4080,"children":4081},{"disabled":438,"type":439},[],{"type":33,"value":469},{"type":27,"tag":46,"props":4084,"children":4086},{"className":4085},[433],[4087,4090],{"type":27,"tag":436,"props":4088,"children":4089},{"disabled":438,"type":439},[],{"type":33,"value":478},{"type":27,"tag":46,"props":4092,"children":4094},{"className":4093},[433],[4095,4098],{"type":27,"tag":436,"props":4096,"children":4097},{"disabled":438,"type":439},[],{"type":33,"value":487},{"type":27,"tag":46,"props":4100,"children":4102},{"className":4101},[433],[4103,4106],{"type":27,"tag":436,"props":4104,"children":4105},{"disabled":438,"type":439},[],{"type":33,"value":496},{"type":27,"tag":46,"props":4108,"children":4110},{"className":4109},[433],[4111,4114],{"type":27,"tag":436,"props":4112,"children":4113},{"disabled":438,"type":439},[],{"type":33,"value":505},{"type":27,"tag":28,"props":4116,"children":4117},{"id":508},[4118],{"type":33,"value":511},{"type":27,"tag":513,"props":4120,"children":4121},{"id":515},[4122],{"type":33,"value":518},{"type":27,"tag":36,"props":4124,"children":4125},{},[4126],{"type":33,"value":523},{"type":27,"tag":513,"props":4128,"children":4129},{"id":526},[4130],{"type":33,"value":529},{"type":27,"tag":36,"props":4132,"children":4133},{},[4134],{"type":33,"value":534},{"type":27,"tag":513,"props":4136,"children":4137},{"id":537},[4138],{"type":33,"value":540},{"type":27,"tag":36,"props":4140,"children":4141},{},[4142],{"type":33,"value":545},{"type":27,"tag":28,"props":4144,"children":4145},{"id":548},[4146],{"type":33,"value":548},{"type":27,"tag":42,"props":4148,"children":4149},{},[4150,4157,4164,4171],{"type":27,"tag":46,"props":4151,"children":4152},{},[4153],{"type":27,"tag":558,"props":4154,"children":4155},{"href":560},[4156],{"type":33,"value":563},{"type":27,"tag":46,"props":4158,"children":4159},{},[4160],{"type":27,"tag":558,"props":4161,"children":4162},{"href":569},[4163],{"type":33,"value":572},{"type":27,"tag":46,"props":4165,"children":4166},{},[4167],{"type":27,"tag":558,"props":4168,"children":4169},{"href":578},[4170],{"type":33,"value":581},{"type":27,"tag":46,"props":4172,"children":4173},{},[4174],{"type":27,"tag":558,"props":4175,"children":4176},{"href":587},[4177],{"type":33,"value":590},{"title":7,"searchDepth":592,"depth":592,"links":4179},[4180,4181,4182,4183,4184,4185,4186,4187,4188,4193],{"id":30,"depth":595,"text":34},{"id":73,"depth":595,"text":76},{"id":194,"depth":595,"text":197},{"id":223,"depth":595,"text":226},{"id":263,"depth":595,"text":266},{"id":325,"depth":595,"text":328},{"id":364,"depth":595,"text":367},{"id":421,"depth":595,"text":424},{"id":508,"depth":595,"text":511,"children":4189},[4190,4191,4192],{"id":515,"depth":592,"text":518},{"id":526,"depth":592,"text":529},{"id":537,"depth":592,"text":540},{"id":548,"depth":595,"text":548},1775441242672]