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