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