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