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