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