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