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