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