[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"i-ri:search-line":3,"i-ri:moon-line":8,"i-ri:sun-line":10,"i-ri:menu-3-line":12,"i-ri:close-line":14,"notes-module-items-react":16,"notes-page-react-11-data-layer\u002F03-optimistic-updates-and-consistency":494,"i-ri:arrow-left-s-line":2302,"i-ri:menu-2-line":2304,"i-ri:arrow-right-s-line":2306,"i-ep:copy-document":2308},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"currentColor\" d=\"m18.031 16.617l4.283 4.282l-1.415 1.415l-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9s9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617m-2.006-.742A6.98 6.98 0 0 0 18 11c0-3.867-3.133-7-7-7s-7 3.133-7 7s3.133 7 7 7a6.98 6.98 0 0 0 4.875-1.975z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cpath fill=\"currentColor\" d=\"M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7m-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938A8 8 0 0 0 4 12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cpath fill=\"currentColor\" d=\"M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12m0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8M11 1h2v3h-2zm0 19h2v3h-2zM3.515 4.929l1.414-1.414L7.05 5.636L5.636 7.05zM16.95 18.364l1.414-1.414l2.121 2.121l-1.414 1.414zm2.121-14.85l1.414 1.415l-2.121 2.121l-1.414-1.414zM5.636 16.95l1.414 1.414l-2.121 2.121l-1.414-1.414zM23 11v2h-3v-2zM4 11v2H1v-2z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm6 7h12v2H9zm-6 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cpath fill=\"currentColor\" d=\"m12 10.587l4.95-4.95l1.414 1.414l-4.95 4.95l4.95 4.95l-1.415 1.414l-4.95-4.95l-4.949 4.95l-1.414-1.415l4.95-4.95l-4.95-4.95L7.05 5.638z\"\u002F>",[17,22,26,30,34,38,42,46,50,54,58,62,66,70,74,78,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,194,198,202,206,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294,298,302,306,310,314,318,322,326,330,334,338,342,346,350,354,358,362,366,370,374,378,382,386,390,394,398,402,406,410,414,418,422,426,430,434,438,442,446,450,454,458,462,466,470,474,478,482,486,490],{"path":18,"title":19,"meta":20},"\u002Fnotes\u002Freact\u002F01-basics\u002F01-how-to-read-this-repo","如何阅读本仓库（学习路径与章节依赖）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},"2026-11-05T09:47:15.000Z",{"path":23,"title":24,"meta":25},"\u002Fnotes\u002Freact\u002F01-basics\u002F02-render-vs-commit","React 渲染两阶段：Render vs Commit（总览）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":27,"title":28,"meta":29},"\u002Fnotes\u002Freact\u002F01-basics\u002F03-update-pipeline-birdview","从“更新”到“页面变更”：一次 setState 的全链路鸟瞰",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":31,"title":32,"meta":33},"\u002Fnotes\u002Freact\u002F01-basics","基础入门（建议先读）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":35,"title":36,"meta":37},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F01-browser-multi-process","浏览器多进程架构",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":39,"title":40,"meta":41},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F02-event-loop-overview","事件循环概述",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":43,"title":44,"meta":45},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F03-call-stack","函数调用栈",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":47,"title":48,"meta":49},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F04-threads","线程介绍",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":51,"title":52,"meta":53},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F05-refresh-rate-and-queues","刷新率与执行队列",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":55,"title":56,"meta":57},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F06-settimeout","setTimeout 原理详解",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":59,"title":60,"meta":61},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F07-promise","Promise 原理详解",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":63,"title":64,"meta":65},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F08-event-loop-in-practice","事件循环机制（综合）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":67,"title":68,"meta":69},"\u002Fnotes\u002Freact\u002F02-event-loop\u002F09-priority-queues","优先级队列",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":71,"title":72,"meta":73},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F01-concept","并发模式：概念",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":75,"title":76,"meta":77},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F02-demo","案例演示并发模式",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":79,"title":80,"meta":81},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F03-high-priority-preemption","高优先级插队",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":83,"title":84,"meta":85},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F04-preemption-vs-interruption","插队的影响、与中断的区别",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":87,"title":88,"meta":89},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F05-shouldYield","中断条件：shouldYield",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":91,"title":92,"meta":93},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F06-react-tasks-and-queues","React 中的任务与队列",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":95,"title":96,"meta":97},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F07-ensureRootIsScheduled","ensureRootIsScheduled",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":99,"title":100,"meta":101},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F08-batch-updates-merge","合并多个更新触发",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":103,"title":104,"meta":105},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F09-taskQueue-lifecycle","taskQueue 队列循环的启动与结束",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":107,"title":108,"meta":109},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F10-time-slicing","时间切片是如何实现的",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":111,"title":112,"meta":113},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F11-workLoop-full","workLoop 循环完整逻辑",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":115,"title":116,"meta":117},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F12-priority-design","React 中的优先级设计",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":119,"title":120,"meta":121},"\u002Fnotes\u002Freact\u002F03-concurrent-mode\u002F13-lane-model","lane 模型",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":123,"title":124,"meta":125},"\u002Fnotes\u002Freact\u002F04-fiber\u002F01-three-node-types","三种节点对象",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":127,"title":128,"meta":129},"\u002Fnotes\u002Freact\u002F04-fiber\u002F02-fiberRootNode","FiberRootNode",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":131,"title":132,"meta":133},"\u002Fnotes\u002Freact\u002F04-fiber\u002F03-fiber-tree","Fiber 树",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":135,"title":136,"meta":137},"\u002Fnotes\u002Freact\u002F04-fiber\u002F04-initial-mount-build","Fiber 树初始构建过程",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":139,"title":140,"meta":141},"\u002Fnotes\u002Freact\u002F04-fiber\u002F05-workLoopConcurrent","workLoopConcurrent",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":143,"title":144,"meta":145},"\u002Fnotes\u002Freact\u002F04-fiber\u002F06-stack-and-workInProgress","栈结构 与 workInProgress",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":147,"title":148,"meta":149},"\u002Fnotes\u002Freact\u002F04-fiber\u002F07-executionContext","执行上下文 executionContext",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":151,"title":152,"meta":153},"\u002Fnotes\u002Freact\u002F04-fiber\u002F08-update-lanes","update lanes",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":155,"title":156,"meta":157},"\u002Fnotes\u002Freact\u002F04-fiber\u002F09-render-lanes","render lanes",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":159,"title":160,"meta":161},"\u002Fnotes\u002Freact\u002F04-fiber\u002F10-fiber-lanes","fiber lanes",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":163,"title":164,"meta":165},"\u002Fnotes\u002Freact\u002F04-fiber\u002F11-flags-appendix","附录：flags",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":167,"title":168,"meta":169},"\u002Fnotes\u002Freact\u002F05-hooks\u002F01-background","hook 背景",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":171,"title":172,"meta":173},"\u002Fnotes\u002Freact\u002F05-hooks\u002F02-categories","hook 分类",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":175,"title":176,"meta":177},"\u002Fnotes\u002Freact\u002F05-hooks\u002F03-closures","hook 与闭包",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":179,"title":180,"meta":181},"\u002Fnotes\u002Freact\u002F05-hooks\u002F04-hooks-and-fiber","hook 与 fiber",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":183,"title":184,"meta":185},"\u002Fnotes\u002Freact\u002F05-hooks\u002F05-renderWithHooks","renderWithHooks",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":187,"title":188,"meta":189},"\u002Fnotes\u002Freact\u002F05-hooks\u002F06-init-state-hook","初始化 State Hook",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":191,"title":192,"meta":193},"\u002Fnotes\u002Freact\u002F05-hooks\u002F07-init-effect-hook","初始化 effect Hook",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":195,"title":196,"meta":197},"\u002Fnotes\u002Freact\u002F05-hooks\u002F08-circular-linked-list","环形链表详解",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":199,"title":200,"meta":201},"\u002Fnotes\u002Freact\u002F05-hooks\u002F09-useReducer-vs-useState","useReducer 与 useState 的区别",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":203,"title":204,"meta":205},"\u002Fnotes\u002Freact\u002F05-hooks\u002F10-update-linked-list","update 链表",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":207,"title":208,"meta":209},"\u002Fnotes\u002Freact\u002F05-hooks\u002F11-mount-state-updates","初始阶段的状态更新",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":211,"title":212,"meta":213},"\u002Fnotes\u002Freact\u002F05-hooks\u002F12-baseQueue-vs-pending","baseQueue 与 queue.pending",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":215,"title":216,"meta":217},"\u002Fnotes\u002Freact\u002F05-hooks\u002F13-dispatch-in-update","更新阶段的 dispatch",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":219,"title":220,"meta":221},"\u002Fnotes\u002Freact\u002F05-hooks\u002F14-commitRoot","commitRoot",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":223,"title":224,"meta":225},"\u002Fnotes\u002Freact\u002F06-diff\u002F01-update-overview","React 更新机制概述",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":227,"title":228,"meta":229},"\u002Fnotes\u002Freact\u002F06-diff\u002F02-diff-strategy","diff 策略",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":231,"title":232,"meta":233},"\u002Fnotes\u002Freact\u002F06-diff\u002F03-node-compare","节点比较方式",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":235,"title":236,"meta":237},"\u002Fnotes\u002Freact\u002F06-diff\u002F04-reconcileChildFibers","reconcileChildFibers",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":239,"title":240,"meta":241},"\u002Fnotes\u002Freact\u002F06-diff\u002F05-reconcileSingleElement","reconcileSingleElement",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":243,"title":244,"meta":245},"\u002Fnotes\u002Freact\u002F06-diff\u002F06-reconcileChildrenArray","reconcileChildrenArray",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":247,"title":248,"meta":249},"\u002Fnotes\u002Freact\u002F06-diff\u002F07-why-not-number-key","为什么不建议使用数字作为 key",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":251,"title":252,"meta":253},"\u002Fnotes\u002Freact\u002F07-event-system\u002F01-overview","事件系统概述",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":255,"title":256,"meta":257},"\u002Fnotes\u002Freact\u002F07-event-system\u002F02-event-plugin","事件插件",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":259,"title":260,"meta":261},"\u002Fnotes\u002Freact\u002F07-event-system\u002F03-event-pooling","事件池",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":263,"title":264,"meta":265},"\u002Fnotes\u002Freact\u002F07-event-system\u002F04-root-listeners","根节点事件绑定",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":267,"title":268,"meta":269},"\u002Fnotes\u002Freact\u002F07-event-system\u002F05-event-dispatch","事件触发",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":271,"title":272,"meta":273},"\u002Fnotes\u002Freact\u002F08-interview\u002F01-useEffect-vs-useLayoutEffect","useEffect 与 useLayoutEffect 的区别",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":275,"title":276,"meta":277},"\u002Fnotes\u002Freact\u002F08-interview\u002F02-why-no-conditional-hooks","为什么 hook 不能写在条件判断里",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":279,"title":280,"meta":281},"\u002Fnotes\u002Freact\u002F08-interview\u002F03-understanding-concurrency","聊聊在 React 中，你对并发模式的理解",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":283,"title":284,"meta":285},"\u002Fnotes\u002Freact\u002F08-interview\u002F04-update-mechanism","聊聊 React 的更新机制原理",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":287,"title":288,"meta":289},"\u002Fnotes\u002Freact\u002F08-interview\u002F05-setState-sync-or-async","setState 是同步还是异步的？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":291,"title":292,"meta":293},"\u002Fnotes\u002Freact\u002F08-interview\u002F06-useState-vs-useReducer","聊聊 useState 与 useReducer 的区别",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":295,"title":296,"meta":297},"\u002Fnotes\u002Freact\u002F08-interview\u002F07-closure-trap","聊聊闭包陷阱问题，以及如何解决",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":299,"title":300,"meta":301},"\u002Fnotes\u002Freact\u002F08-interview\u002F08-time-slicing-details","时间分片是如何实现的（任务拆分\u002F中断\u002F恢复）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":303,"title":304,"meta":305},"\u002Fnotes\u002Freact\u002F08-interview\u002F09-batched-updates-useState","聊聊 useState 的批处理更新机制",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":307,"title":308,"meta":309},"\u002Fnotes\u002Freact\u002F08-interview\u002F10-jsx-to-dom","JSX 是如何转变成为真实 DOM 的",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":311,"title":312,"meta":313},"\u002Fnotes\u002Freact\u002F08-interview\u002F11-useCallback-impl","useCallback 的实现原理是什么",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":315,"title":316,"meta":317},"\u002Fnotes\u002Freact\u002F08-interview\u002F12-scheduling-mechanism","详细聊聊 React 的调度机制",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":319,"title":320,"meta":321},"\u002Fnotes\u002Freact\u002F08-interview\u002F13-priority-and-lanes","聊聊 React 的优先级机制（Lane 模型）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":323,"title":324,"meta":325},"\u002Fnotes\u002Freact\u002F08-interview\u002F14-diff-algorithm","React 的 Diff 算法是什么样的",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":327,"title":328,"meta":329},"\u002Fnotes\u002Freact\u002F08-interview\u002F15-event-system","详细聊聊 React 的事件系统是如何实现的",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":331,"title":332,"meta":333},"\u002Fnotes\u002Freact\u002F08-interview\u002F16-portals-bubbling","React Portals 上的事件是否会冒泡到父组件，原理是什么",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":335,"title":336,"meta":337},"\u002Fnotes\u002Freact\u002F08-interview\u002F17-useEffect-impl","useEffect 的实现原理是什么",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":339,"title":340,"meta":341},"\u002Fnotes\u002Freact\u002F08-interview\u002F18-useContext-impl","请聊聊 useContext 的实现原理",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":343,"title":344,"meta":345},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice\u002F01-automatic-batching-and-flushsync","React 18 自动批处理（Automatic Batching）与 flushSync",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":347,"title":348,"meta":349},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice\u002F02-strictmode-and-idempotent-effects","React StrictMode：为什么会“双调用”？（源码\u002F机制视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":351,"title":352,"meta":353},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice\u002F03-transition-useTransition-useDeferredValue","Transition：startTransition \u002F useTransition \u002F useDeferredValue（机制视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":355,"title":356,"meta":357},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice\u002F04-suspense-loading-boundary-design","Suspense：加载态边界如何设计（机制\u002F架构视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":359,"title":360,"meta":361},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice\u002F05-error-boundary-reporting-and-recovery","Error Boundary：兜底、重试与上报（自研 vs Sentry）（机制\u002F架构视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":363,"title":364,"meta":365},"\u002Fnotes\u002Freact\u002F09-react-18-in-practice","React 18 实战（并发特性与默认行为）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":367,"title":368,"meta":369},"\u002Fnotes\u002Freact\u002F10-routing-and-navigation\u002F01-routing-and-url-state-mental-model","路由与 URL State：统一心智模型（架构师视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":371,"title":372,"meta":373},"\u002Fnotes\u002Freact\u002F10-routing-and-navigation\u002F02-tanstack-router-route-tree-search-loader-prefetch","TanStack Router（Vite \u002F SPA）：Route Tree \u002F Search \u002F Loader \u002F Prefetch（落地篇）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":375,"title":376,"meta":377},"\u002Fnotes\u002Freact\u002F10-routing-and-navigation\u002F03-next-app-router-segments-layout-loading-error-not-found-prefetch","Next.js App Router：Segments \u002F Layouts \u002F Loading-Error-NotFound \u002F Prefetch（落地篇）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":379,"title":380,"meta":381},"\u002Fnotes\u002Freact\u002F10-routing-and-navigation","路由与导航（TanStack Router \u002F Next App Router）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":383,"title":384,"meta":385},"\u002Fnotes\u002Freact\u002F11-data-layer\u002F01-state-layering-ui-server-url-global","状态分层：UI \u002F Server \u002F URL \u002F Global（架构师视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":387,"title":388,"meta":389},"\u002Fnotes\u002Freact\u002F11-data-layer\u002F02-tanstack-query-caching-and-invalidation","TanStack Query：缓存模型与失效策略（Caching & Invalidation）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":391,"title":392,"meta":393},"\u002Fnotes\u002Freact\u002F11-data-layer\u002F03-optimistic-updates-and-consistency","TanStack Query：乐观更新与一致性（Optimistic Updates & Consistency）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":395,"title":396,"meta":397},"\u002Fnotes\u002Freact\u002F11-data-layer\u002F04-form-architecture-react-hook-form-validation-performance-testability","表单架构：React Hook Form 的受控\u002F非受控、校验、性能与可测试性（架构师视角）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":399,"title":400,"meta":401},"\u002Fnotes\u002Freact\u002F11-data-layer","数据层（Server State \u002F 表单 \u002F 状态分层）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":403,"title":404,"meta":405},"\u002Fnotes\u002Freact\u002F12-rendering-and-delivery\u002F01-hydration-mismatch","Hydration mismatch（React \u002F Next.js）：成因、检测、排查清单与工程化规避",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":407,"title":408,"meta":409},"\u002Fnotes\u002Freact\u002F12-rendering-and-delivery\u002F01-nextjs-cache-revalidate-consistency-online-not-updating","Next.js 缓存\u002F重验证与一致性：避免「线上怎么不更新」事故（App Router）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":411,"title":412,"meta":413},"\u002Fnotes\u002Freact\u002F12-rendering-and-delivery\u002F02-csr-ssr-ssg-isr-how-to-choose","CSR vs SSR vs SSG vs ISR：怎么选？边界是什么？（体验 + 成本 + 一致性）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":415,"title":416,"meta":417},"\u002Fnotes\u002Freact\u002F12-rendering-and-delivery\u002F04-streaming-ssr-with-suspense","Streaming SSR + Suspense：提升前端感知性能的利器",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":419,"title":420,"meta":421},"\u002Fnotes\u002Freact\u002F12-rendering-and-delivery","渲染与交付（CSR\u002FSSR\u002FStreaming\u002FHydration\u002F缓存）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":423,"title":424,"meta":425},"\u002Fnotes\u002Freact\u002F13-production-readiness\u002F01-observability-logging-metrics-tracing","前端可观测性体系：日志、指标与链路追踪",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":427,"title":428,"meta":429},"\u002Fnotes\u002Freact\u002F13-production-readiness\u002F02-auth-and-web-security","鉴权与 Web 安全：OAuth, JWT, XSS, CSRF, CSP",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":431,"title":432,"meta":433},"\u002Fnotes\u002Freact\u002F13-production-readiness","生产可用性（错误\u002F可观测\u002F鉴权\u002F安全）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":435,"title":436,"meta":437},"\u002Fnotes\u002Freact\u002F14-performance\u002F01-performance-playbook-and-metrics","前端性能定位 Playbook：从指标到归因",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":439,"title":440,"meta":441},"\u002Fnotes\u002Freact\u002F14-performance\u002F02-rendering-optimization-memo-context-virtualization","React 渲染性能优化：Memo, Context, 与虚拟化",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":443,"title":444,"meta":445},"\u002Fnotes\u002Freact\u002F14-performance\u002F03-asset-and-bundle-optimization","资源与 Bundle 优化：代码分割、图片与 CDN",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":447,"title":448,"meta":449},"\u002Fnotes\u002Freact\u002F14-performance","性能体系（定位 → 优化 → 度量）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":451,"title":452,"meta":453},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture\u002F01-component-system-and-design-system","组件体系与设计系统：构建规模化前端的基石",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":455,"title":456,"meta":457},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture\u002F02-testing-strategy-rtl-e2e-testability","前端测试体系：RTL, E2E 与可测性设计",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":459,"title":460,"meta":461},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture\u002F03-monorepo-and-dependency-management","Monorepo 与依赖治理：pnpm 和 Turborepo",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":463,"title":464,"meta":465},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture\u002F04-cicd-and-quality-gates","CI\u002FCD 与质量门禁：构建可靠的自动化流程",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":467,"title":468,"meta":469},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture\u002F05-bff-and-api-design","BFF 与 API 设计：前端架构师的视角",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":471,"title":472,"meta":473},"\u002Fnotes\u002Freact\u002F15-engineering-and-architecture","工程与架构治理（规模化）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":475,"title":476,"meta":477},"\u002Fnotes\u002Freact\u002F16-advanced\u002F01-rsc-and-server-actions-deep-dive","RSC\u002FServer Actions 深水区：边界、约束与缓存一致性",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":479,"title":480,"meta":481},"\u002Fnotes\u002Freact\u002F16-advanced\u002F02-micro-frontend-governance","微前端治理：样式、路由与通信",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":483,"title":484,"meta":485},"\u002Fnotes\u002Freact\u002F16-advanced\u002F03-realtime-and-offline","实时与离线：WebSocket, PWA 与 Service Worker",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":487,"title":488,"meta":489},"\u002Fnotes\u002Freact\u002F16-advanced\u002F04-a11y-i18n-compliance","a11y, i18n 与合规：构建负责任的全球化应用",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":491,"title":492,"meta":493},"\u002Fnotes\u002Freact\u002F16-advanced","进阶专题（按业务需要选做）",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"id":495,"type":496,"sourcePath":497,"routePath":391,"path":391,"title":392,"updatedAt":21,"meta":498,"body":500,"toc":2283},"bm90ZTpub3Rlcy9yZWFjdC8xMS1kYXRhLWxheWVyLzAzLW9wdGltaXN0aWMtdXBkYXRlcy1hbmQtY29uc2lzdGVuY3kubWQ","note","notes\u002Freact\u002F11-data-layer\u002F03-optimistic-updates-and-consistency.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":392,"updatedAt":21,"lastUpdated":21},"react\u002F11-data-layer\u002F03-optimistic-updates-and-consistency",{"type":501,"children":502},"root",[503,511,543,550,555,600,608,614,619,685,692,1567,1575,1581,1586,1592,1604,1623,1634,1673,1684,1697,1703,1723,1729,1740,1760,1771,1798,1809,1817,1828,1836,1847,1881,1887,1893,1918,1924,1967,1972,1980,1986,2046,2052,2085,2091,2212,2217,2277],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"tanstack-query乐观更新与一致性optimistic-updates-consistency",[509],{"type":510,"value":392},"text",{"type":504,"tag":512,"props":513,"children":514},"blockquote",{},[515],{"type":504,"tag":516,"props":517,"children":518},"p",{},[519,521,527,529,534,536,541],{"type":510,"value":520},"一句话结论：乐观更新不是“让缓存提前变真”，而是",{"type":504,"tag":522,"props":523,"children":524},"strong",{},[525],{"type":510,"value":526},"先用本地推演结果缩短交互反馈",{"type":510,"value":528},"，再用 ",{"type":504,"tag":522,"props":530,"children":531},{},[532],{"type":510,"value":533},"回滚（rollback）+ 失效刷新（invalidate\u002Frefetch）+ 冲突处理（reconcile）",{"type":510,"value":535}," 保证最终以服务端为准的一致性；工程关键在于：",{"type":504,"tag":522,"props":537,"children":538},{},[539],{"type":510,"value":540},"更新范围（哪些 queryKey）、竞态控制（cancel\u002F乱序返回）、以及把最终一致性做成可预期的规则",{"type":510,"value":542},"。",{"type":504,"tag":544,"props":545,"children":547},"h2",{"id":546},"_1-先对齐术语你到底在一致什么",[548],{"type":510,"value":549},"1. 先对齐术语：你到底在一致什么？",{"type":504,"tag":516,"props":551,"children":552},{},[553],{"type":510,"value":554},"在真实业务里你通常不是追求“强一致”，而是在用户体验与正确性之间做权衡。",{"type":504,"tag":556,"props":557,"children":558},"ul",{},[559,570,580,590],{"type":504,"tag":560,"props":561,"children":562},"li",{},[563,568],{"type":504,"tag":522,"props":564,"children":565},{},[566],{"type":510,"value":567},"source of truth（真相）",{"type":510,"value":569},"：服务端",{"type":504,"tag":560,"props":571,"children":572},{},[573,578],{"type":504,"tag":522,"props":574,"children":575},{},[576],{"type":510,"value":577},"query cache（副本）",{"type":510,"value":579},"：客户端缓存（可被 invalidate）",{"type":504,"tag":560,"props":581,"children":582},{},[583,588],{"type":504,"tag":522,"props":584,"children":585},{},[586],{"type":510,"value":587},"optimistic state（乐观态）",{"type":510,"value":589},"：为了体验，提前把 UI 变成“看起来成功”的状态",{"type":504,"tag":560,"props":591,"children":592},{},[593,598],{"type":504,"tag":522,"props":594,"children":595},{},[596],{"type":510,"value":597},"final state（最终态）",{"type":510,"value":599},"：服务端确认后的状态（可能与乐观态不同）",{"type":504,"tag":512,"props":601,"children":602},{},[603],{"type":504,"tag":516,"props":604,"children":605},{},[606],{"type":510,"value":607},"经验：乐观更新的本质是“先让 UI 好用”，而不是“绕过服务端”。",{"type":504,"tag":544,"props":609,"children":611},{"id":610},"_2-tanstack-query-的标准乐观更新生命周期usemutation",[612],{"type":510,"value":613},"2. TanStack Query 的标准乐观更新生命周期（useMutation）",{"type":504,"tag":516,"props":615,"children":616},{},[617],{"type":510,"value":618},"一套可复用的心智模型：",{"type":504,"tag":556,"props":620,"children":621},{},[622,652,663,674],{"type":504,"tag":560,"props":623,"children":624},{},[625,632,634],{"type":504,"tag":626,"props":627,"children":629},"code",{"className":628},[],[630],{"type":510,"value":631},"onMutate",{"type":510,"value":633},"：写入乐观结果前的准备阶段\n",{"type":504,"tag":556,"props":635,"children":636},{},[637,642,647],{"type":504,"tag":560,"props":638,"children":639},{},[640],{"type":510,"value":641},"取消相关查询（避免旧响应覆盖）",{"type":504,"tag":560,"props":643,"children":644},{},[645],{"type":510,"value":646},"读取快照（用于回滚）",{"type":504,"tag":560,"props":648,"children":649},{},[650],{"type":510,"value":651},"写入乐观结果（setQueryData）",{"type":504,"tag":560,"props":653,"children":654},{},[655,661],{"type":504,"tag":626,"props":656,"children":658},{"className":657},[],[659],{"type":510,"value":660},"onError",{"type":510,"value":662},"：失败回滚",{"type":504,"tag":560,"props":664,"children":665},{},[666,672],{"type":504,"tag":626,"props":667,"children":669},{"className":668},[],[670],{"type":510,"value":671},"onSuccess",{"type":510,"value":673},"：用服务端回包 reconcile（必要时覆盖乐观结果）",{"type":504,"tag":560,"props":675,"children":676},{},[677,683],{"type":504,"tag":626,"props":678,"children":680},{"className":679},[],[681],{"type":510,"value":682},"onSettled",{"type":510,"value":684},"：兜底 invalidate，做最终一致性校准",{"type":504,"tag":686,"props":687,"children":689},"h3",{"id":688},"_21-最小代码骨架示意",[690],{"type":510,"value":691},"2.1 最小代码骨架（示意）",{"type":504,"tag":693,"props":694,"children":699},"pre",{"className":695,"code":696,"language":697,"meta":698,"style":698},"language-ts shiki shiki-themes github-light github-dark","import { useMutation, useQueryClient } from '@tanstack\u002Freact-query'\n\nfunction useToggleTodoDone() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: async ({ id, done }: { id: string; done: boolean }) => {\n      return api.todo.update(id, { done })\n    },\n\n    onMutate: async ({ id, done }) => {\n      \u002F\u002F 1) 避免竞态：取消可能覆盖本次乐观写入的 in-flight 请求\n      await queryClient.cancelQueries({ queryKey: ['todo', id] })\n\n      \u002F\u002F 2) 快照：用于回滚\n      const prev = queryClient.getQueryData(['todo', id])\n\n      \u002F\u002F 3) 写入乐观结果（detail）\n      queryClient.setQueryData(['todo', id], (old: any) => ({\n        ...old,\n        done,\n        __optimistic: true,\n      }))\n\n      return { prev }\n    },\n\n    onError: (_err, { id }, ctx) => {\n      \u002F\u002F 4) 失败回滚\n      if (ctx?.prev) queryClient.setQueryData(['todo', id], ctx.prev)\n    },\n\n    onSuccess: (serverTodo, { id }) => {\n      \u002F\u002F 5) 用服务端回包做 reconcile（以服务端为准）\n      queryClient.setQueryData(['todo', id], serverTodo)\n    },\n\n    onSettled: (_data, _err, { id }) => {\n      \u002F\u002F 6) 兜底最终一致性（特别是你没在 onSuccess 里覆盖时）\n      queryClient.invalidateQueries({ queryKey: ['todo', id] })\n    },\n  })\n}\n","ts","",[700],{"type":504,"tag":626,"props":701,"children":702},{"__ignoreMap":698},[703,732,742,762,792,800,819,920,944,953,961,1006,1016,1050,1058,1067,1108,1116,1125,1180,1194,1203,1222,1231,1238,1251,1259,1267,1317,1326,1357,1365,1373,1411,1420,1445,1453,1461,1507,1516,1541,1549,1558],{"type":504,"tag":704,"props":705,"children":708},"span",{"class":706,"line":707},"line",1,[709,715,721,726],{"type":504,"tag":704,"props":710,"children":712},{"style":711},"--shiki-default:#D73A49;--shiki-dark:#F97583",[713],{"type":510,"value":714},"import",{"type":504,"tag":704,"props":716,"children":718},{"style":717},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[719],{"type":510,"value":720}," { useMutation, useQueryClient } ",{"type":504,"tag":704,"props":722,"children":723},{"style":711},[724],{"type":510,"value":725},"from",{"type":504,"tag":704,"props":727,"children":729},{"style":728},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[730],{"type":510,"value":731}," '@tanstack\u002Freact-query'\n",{"type":504,"tag":704,"props":733,"children":735},{"class":706,"line":734},2,[736],{"type":504,"tag":704,"props":737,"children":739},{"emptyLinePlaceholder":738},true,[740],{"type":510,"value":741},"\n",{"type":504,"tag":704,"props":743,"children":745},{"class":706,"line":744},3,[746,751,757],{"type":504,"tag":704,"props":747,"children":748},{"style":711},[749],{"type":510,"value":750},"function",{"type":504,"tag":704,"props":752,"children":754},{"style":753},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[755],{"type":510,"value":756}," useToggleTodoDone",{"type":504,"tag":704,"props":758,"children":759},{"style":717},[760],{"type":510,"value":761},"() {\n",{"type":504,"tag":704,"props":763,"children":765},{"class":706,"line":764},4,[766,771,777,782,787],{"type":504,"tag":704,"props":767,"children":768},{"style":711},[769],{"type":510,"value":770},"  const",{"type":504,"tag":704,"props":772,"children":774},{"style":773},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[775],{"type":510,"value":776}," queryClient",{"type":504,"tag":704,"props":778,"children":779},{"style":711},[780],{"type":510,"value":781}," =",{"type":504,"tag":704,"props":783,"children":784},{"style":753},[785],{"type":510,"value":786}," useQueryClient",{"type":504,"tag":704,"props":788,"children":789},{"style":717},[790],{"type":510,"value":791},"()\n",{"type":504,"tag":704,"props":793,"children":795},{"class":706,"line":794},5,[796],{"type":504,"tag":704,"props":797,"children":798},{"emptyLinePlaceholder":738},[799],{"type":510,"value":741},{"type":504,"tag":704,"props":801,"children":803},{"class":706,"line":802},6,[804,809,814],{"type":504,"tag":704,"props":805,"children":806},{"style":711},[807],{"type":510,"value":808},"  return",{"type":504,"tag":704,"props":810,"children":811},{"style":753},[812],{"type":510,"value":813}," useMutation",{"type":504,"tag":704,"props":815,"children":816},{"style":717},[817],{"type":510,"value":818},"({\n",{"type":504,"tag":704,"props":820,"children":822},{"class":706,"line":821},7,[823,828,833,838,843,849,854,859,864,869,874,878,882,887,892,896,900,905,910,915],{"type":504,"tag":704,"props":824,"children":825},{"style":753},[826],{"type":510,"value":827},"    mutationFn",{"type":504,"tag":704,"props":829,"children":830},{"style":717},[831],{"type":510,"value":832},": ",{"type":504,"tag":704,"props":834,"children":835},{"style":711},[836],{"type":510,"value":837},"async",{"type":504,"tag":704,"props":839,"children":840},{"style":717},[841],{"type":510,"value":842}," ({ ",{"type":504,"tag":704,"props":844,"children":846},{"style":845},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[847],{"type":510,"value":848},"id",{"type":504,"tag":704,"props":850,"children":851},{"style":717},[852],{"type":510,"value":853},", ",{"type":504,"tag":704,"props":855,"children":856},{"style":845},[857],{"type":510,"value":858},"done",{"type":504,"tag":704,"props":860,"children":861},{"style":717},[862],{"type":510,"value":863}," }",{"type":504,"tag":704,"props":865,"children":866},{"style":711},[867],{"type":510,"value":868},":",{"type":504,"tag":704,"props":870,"children":871},{"style":717},[872],{"type":510,"value":873}," { ",{"type":504,"tag":704,"props":875,"children":876},{"style":845},[877],{"type":510,"value":848},{"type":504,"tag":704,"props":879,"children":880},{"style":711},[881],{"type":510,"value":868},{"type":504,"tag":704,"props":883,"children":884},{"style":773},[885],{"type":510,"value":886}," string",{"type":504,"tag":704,"props":888,"children":889},{"style":717},[890],{"type":510,"value":891},"; ",{"type":504,"tag":704,"props":893,"children":894},{"style":845},[895],{"type":510,"value":858},{"type":504,"tag":704,"props":897,"children":898},{"style":711},[899],{"type":510,"value":868},{"type":504,"tag":704,"props":901,"children":902},{"style":773},[903],{"type":510,"value":904}," boolean",{"type":504,"tag":704,"props":906,"children":907},{"style":717},[908],{"type":510,"value":909}," }) ",{"type":504,"tag":704,"props":911,"children":912},{"style":711},[913],{"type":510,"value":914},"=>",{"type":504,"tag":704,"props":916,"children":917},{"style":717},[918],{"type":510,"value":919}," {\n",{"type":504,"tag":704,"props":921,"children":923},{"class":706,"line":922},8,[924,929,934,939],{"type":504,"tag":704,"props":925,"children":926},{"style":711},[927],{"type":510,"value":928},"      return",{"type":504,"tag":704,"props":930,"children":931},{"style":717},[932],{"type":510,"value":933}," api.todo.",{"type":504,"tag":704,"props":935,"children":936},{"style":753},[937],{"type":510,"value":938},"update",{"type":504,"tag":704,"props":940,"children":941},{"style":717},[942],{"type":510,"value":943},"(id, { done })\n",{"type":504,"tag":704,"props":945,"children":947},{"class":706,"line":946},9,[948],{"type":504,"tag":704,"props":949,"children":950},{"style":717},[951],{"type":510,"value":952},"    },\n",{"type":504,"tag":704,"props":954,"children":956},{"class":706,"line":955},10,[957],{"type":504,"tag":704,"props":958,"children":959},{"emptyLinePlaceholder":738},[960],{"type":510,"value":741},{"type":504,"tag":704,"props":962,"children":964},{"class":706,"line":963},11,[965,970,974,978,982,986,990,994,998,1002],{"type":504,"tag":704,"props":966,"children":967},{"style":753},[968],{"type":510,"value":969},"    onMutate",{"type":504,"tag":704,"props":971,"children":972},{"style":717},[973],{"type":510,"value":832},{"type":504,"tag":704,"props":975,"children":976},{"style":711},[977],{"type":510,"value":837},{"type":504,"tag":704,"props":979,"children":980},{"style":717},[981],{"type":510,"value":842},{"type":504,"tag":704,"props":983,"children":984},{"style":845},[985],{"type":510,"value":848},{"type":504,"tag":704,"props":987,"children":988},{"style":717},[989],{"type":510,"value":853},{"type":504,"tag":704,"props":991,"children":992},{"style":845},[993],{"type":510,"value":858},{"type":504,"tag":704,"props":995,"children":996},{"style":717},[997],{"type":510,"value":909},{"type":504,"tag":704,"props":999,"children":1000},{"style":711},[1001],{"type":510,"value":914},{"type":504,"tag":704,"props":1003,"children":1004},{"style":717},[1005],{"type":510,"value":919},{"type":504,"tag":704,"props":1007,"children":1009},{"class":706,"line":1008},12,[1010],{"type":504,"tag":704,"props":1011,"children":1013},{"style":1012},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[1014],{"type":510,"value":1015},"      \u002F\u002F 1) 避免竞态：取消可能覆盖本次乐观写入的 in-flight 请求\n",{"type":504,"tag":704,"props":1017,"children":1019},{"class":706,"line":1018},13,[1020,1025,1030,1035,1040,1045],{"type":504,"tag":704,"props":1021,"children":1022},{"style":711},[1023],{"type":510,"value":1024},"      await",{"type":504,"tag":704,"props":1026,"children":1027},{"style":717},[1028],{"type":510,"value":1029}," queryClient.",{"type":504,"tag":704,"props":1031,"children":1032},{"style":753},[1033],{"type":510,"value":1034},"cancelQueries",{"type":504,"tag":704,"props":1036,"children":1037},{"style":717},[1038],{"type":510,"value":1039},"({ queryKey: [",{"type":504,"tag":704,"props":1041,"children":1042},{"style":728},[1043],{"type":510,"value":1044},"'todo'",{"type":504,"tag":704,"props":1046,"children":1047},{"style":717},[1048],{"type":510,"value":1049},", id] })\n",{"type":504,"tag":704,"props":1051,"children":1053},{"class":706,"line":1052},14,[1054],{"type":504,"tag":704,"props":1055,"children":1056},{"emptyLinePlaceholder":738},[1057],{"type":510,"value":741},{"type":504,"tag":704,"props":1059,"children":1061},{"class":706,"line":1060},15,[1062],{"type":504,"tag":704,"props":1063,"children":1064},{"style":1012},[1065],{"type":510,"value":1066},"      \u002F\u002F 2) 快照：用于回滚\n",{"type":504,"tag":704,"props":1068,"children":1070},{"class":706,"line":1069},16,[1071,1076,1081,1085,1089,1094,1099,1103],{"type":504,"tag":704,"props":1072,"children":1073},{"style":711},[1074],{"type":510,"value":1075},"      const",{"type":504,"tag":704,"props":1077,"children":1078},{"style":773},[1079],{"type":510,"value":1080}," prev",{"type":504,"tag":704,"props":1082,"children":1083},{"style":711},[1084],{"type":510,"value":781},{"type":504,"tag":704,"props":1086,"children":1087},{"style":717},[1088],{"type":510,"value":1029},{"type":504,"tag":704,"props":1090,"children":1091},{"style":753},[1092],{"type":510,"value":1093},"getQueryData",{"type":504,"tag":704,"props":1095,"children":1096},{"style":717},[1097],{"type":510,"value":1098},"([",{"type":504,"tag":704,"props":1100,"children":1101},{"style":728},[1102],{"type":510,"value":1044},{"type":504,"tag":704,"props":1104,"children":1105},{"style":717},[1106],{"type":510,"value":1107},", id])\n",{"type":504,"tag":704,"props":1109,"children":1111},{"class":706,"line":1110},17,[1112],{"type":504,"tag":704,"props":1113,"children":1114},{"emptyLinePlaceholder":738},[1115],{"type":510,"value":741},{"type":504,"tag":704,"props":1117,"children":1119},{"class":706,"line":1118},18,[1120],{"type":504,"tag":704,"props":1121,"children":1122},{"style":1012},[1123],{"type":510,"value":1124},"      \u002F\u002F 3) 写入乐观结果（detail）\n",{"type":504,"tag":704,"props":1126,"children":1128},{"class":706,"line":1127},19,[1129,1134,1139,1143,1147,1152,1157,1161,1166,1171,1175],{"type":504,"tag":704,"props":1130,"children":1131},{"style":717},[1132],{"type":510,"value":1133},"      queryClient.",{"type":504,"tag":704,"props":1135,"children":1136},{"style":753},[1137],{"type":510,"value":1138},"setQueryData",{"type":504,"tag":704,"props":1140,"children":1141},{"style":717},[1142],{"type":510,"value":1098},{"type":504,"tag":704,"props":1144,"children":1145},{"style":728},[1146],{"type":510,"value":1044},{"type":504,"tag":704,"props":1148,"children":1149},{"style":717},[1150],{"type":510,"value":1151},", id], (",{"type":504,"tag":704,"props":1153,"children":1154},{"style":845},[1155],{"type":510,"value":1156},"old",{"type":504,"tag":704,"props":1158,"children":1159},{"style":711},[1160],{"type":510,"value":868},{"type":504,"tag":704,"props":1162,"children":1163},{"style":773},[1164],{"type":510,"value":1165}," any",{"type":504,"tag":704,"props":1167,"children":1168},{"style":717},[1169],{"type":510,"value":1170},") ",{"type":504,"tag":704,"props":1172,"children":1173},{"style":711},[1174],{"type":510,"value":914},{"type":504,"tag":704,"props":1176,"children":1177},{"style":717},[1178],{"type":510,"value":1179}," ({\n",{"type":504,"tag":704,"props":1181,"children":1183},{"class":706,"line":1182},20,[1184,1189],{"type":504,"tag":704,"props":1185,"children":1186},{"style":711},[1187],{"type":510,"value":1188},"        ...",{"type":504,"tag":704,"props":1190,"children":1191},{"style":717},[1192],{"type":510,"value":1193},"old,\n",{"type":504,"tag":704,"props":1195,"children":1197},{"class":706,"line":1196},21,[1198],{"type":504,"tag":704,"props":1199,"children":1200},{"style":717},[1201],{"type":510,"value":1202},"        done,\n",{"type":504,"tag":704,"props":1204,"children":1206},{"class":706,"line":1205},22,[1207,1212,1217],{"type":504,"tag":704,"props":1208,"children":1209},{"style":717},[1210],{"type":510,"value":1211},"        __optimistic: ",{"type":504,"tag":704,"props":1213,"children":1214},{"style":773},[1215],{"type":510,"value":1216},"true",{"type":504,"tag":704,"props":1218,"children":1219},{"style":717},[1220],{"type":510,"value":1221},",\n",{"type":504,"tag":704,"props":1223,"children":1225},{"class":706,"line":1224},23,[1226],{"type":504,"tag":704,"props":1227,"children":1228},{"style":717},[1229],{"type":510,"value":1230},"      }))\n",{"type":504,"tag":704,"props":1232,"children":1233},{"class":706,"line":5},[1234],{"type":504,"tag":704,"props":1235,"children":1236},{"emptyLinePlaceholder":738},[1237],{"type":510,"value":741},{"type":504,"tag":704,"props":1239,"children":1241},{"class":706,"line":1240},25,[1242,1246],{"type":504,"tag":704,"props":1243,"children":1244},{"style":711},[1245],{"type":510,"value":928},{"type":504,"tag":704,"props":1247,"children":1248},{"style":717},[1249],{"type":510,"value":1250}," { prev }\n",{"type":504,"tag":704,"props":1252,"children":1254},{"class":706,"line":1253},26,[1255],{"type":504,"tag":704,"props":1256,"children":1257},{"style":717},[1258],{"type":510,"value":952},{"type":504,"tag":704,"props":1260,"children":1262},{"class":706,"line":1261},27,[1263],{"type":504,"tag":704,"props":1264,"children":1265},{"emptyLinePlaceholder":738},[1266],{"type":510,"value":741},{"type":504,"tag":704,"props":1268,"children":1270},{"class":706,"line":1269},28,[1271,1276,1281,1286,1291,1295,1300,1305,1309,1313],{"type":504,"tag":704,"props":1272,"children":1273},{"style":753},[1274],{"type":510,"value":1275},"    onError",{"type":504,"tag":704,"props":1277,"children":1278},{"style":717},[1279],{"type":510,"value":1280},": (",{"type":504,"tag":704,"props":1282,"children":1283},{"style":845},[1284],{"type":510,"value":1285},"_err",{"type":504,"tag":704,"props":1287,"children":1288},{"style":717},[1289],{"type":510,"value":1290},", { ",{"type":504,"tag":704,"props":1292,"children":1293},{"style":845},[1294],{"type":510,"value":848},{"type":504,"tag":704,"props":1296,"children":1297},{"style":717},[1298],{"type":510,"value":1299}," }, ",{"type":504,"tag":704,"props":1301,"children":1302},{"style":845},[1303],{"type":510,"value":1304},"ctx",{"type":504,"tag":704,"props":1306,"children":1307},{"style":717},[1308],{"type":510,"value":1170},{"type":504,"tag":704,"props":1310,"children":1311},{"style":711},[1312],{"type":510,"value":914},{"type":504,"tag":704,"props":1314,"children":1315},{"style":717},[1316],{"type":510,"value":919},{"type":504,"tag":704,"props":1318,"children":1320},{"class":706,"line":1319},29,[1321],{"type":504,"tag":704,"props":1322,"children":1323},{"style":1012},[1324],{"type":510,"value":1325},"      \u002F\u002F 4) 失败回滚\n",{"type":504,"tag":704,"props":1327,"children":1329},{"class":706,"line":1328},30,[1330,1335,1340,1344,1348,1352],{"type":504,"tag":704,"props":1331,"children":1332},{"style":711},[1333],{"type":510,"value":1334},"      if",{"type":504,"tag":704,"props":1336,"children":1337},{"style":717},[1338],{"type":510,"value":1339}," (ctx?.prev) queryClient.",{"type":504,"tag":704,"props":1341,"children":1342},{"style":753},[1343],{"type":510,"value":1138},{"type":504,"tag":704,"props":1345,"children":1346},{"style":717},[1347],{"type":510,"value":1098},{"type":504,"tag":704,"props":1349,"children":1350},{"style":728},[1351],{"type":510,"value":1044},{"type":504,"tag":704,"props":1353,"children":1354},{"style":717},[1355],{"type":510,"value":1356},", id], ctx.prev)\n",{"type":504,"tag":704,"props":1358,"children":1360},{"class":706,"line":1359},31,[1361],{"type":504,"tag":704,"props":1362,"children":1363},{"style":717},[1364],{"type":510,"value":952},{"type":504,"tag":704,"props":1366,"children":1368},{"class":706,"line":1367},32,[1369],{"type":504,"tag":704,"props":1370,"children":1371},{"emptyLinePlaceholder":738},[1372],{"type":510,"value":741},{"type":504,"tag":704,"props":1374,"children":1376},{"class":706,"line":1375},33,[1377,1382,1386,1391,1395,1399,1403,1407],{"type":504,"tag":704,"props":1378,"children":1379},{"style":753},[1380],{"type":510,"value":1381},"    onSuccess",{"type":504,"tag":704,"props":1383,"children":1384},{"style":717},[1385],{"type":510,"value":1280},{"type":504,"tag":704,"props":1387,"children":1388},{"style":845},[1389],{"type":510,"value":1390},"serverTodo",{"type":504,"tag":704,"props":1392,"children":1393},{"style":717},[1394],{"type":510,"value":1290},{"type":504,"tag":704,"props":1396,"children":1397},{"style":845},[1398],{"type":510,"value":848},{"type":504,"tag":704,"props":1400,"children":1401},{"style":717},[1402],{"type":510,"value":909},{"type":504,"tag":704,"props":1404,"children":1405},{"style":711},[1406],{"type":510,"value":914},{"type":504,"tag":704,"props":1408,"children":1409},{"style":717},[1410],{"type":510,"value":919},{"type":504,"tag":704,"props":1412,"children":1414},{"class":706,"line":1413},34,[1415],{"type":504,"tag":704,"props":1416,"children":1417},{"style":1012},[1418],{"type":510,"value":1419},"      \u002F\u002F 5) 用服务端回包做 reconcile（以服务端为准）\n",{"type":504,"tag":704,"props":1421,"children":1423},{"class":706,"line":1422},35,[1424,1428,1432,1436,1440],{"type":504,"tag":704,"props":1425,"children":1426},{"style":717},[1427],{"type":510,"value":1133},{"type":504,"tag":704,"props":1429,"children":1430},{"style":753},[1431],{"type":510,"value":1138},{"type":504,"tag":704,"props":1433,"children":1434},{"style":717},[1435],{"type":510,"value":1098},{"type":504,"tag":704,"props":1437,"children":1438},{"style":728},[1439],{"type":510,"value":1044},{"type":504,"tag":704,"props":1441,"children":1442},{"style":717},[1443],{"type":510,"value":1444},", id], serverTodo)\n",{"type":504,"tag":704,"props":1446,"children":1448},{"class":706,"line":1447},36,[1449],{"type":504,"tag":704,"props":1450,"children":1451},{"style":717},[1452],{"type":510,"value":952},{"type":504,"tag":704,"props":1454,"children":1456},{"class":706,"line":1455},37,[1457],{"type":504,"tag":704,"props":1458,"children":1459},{"emptyLinePlaceholder":738},[1460],{"type":510,"value":741},{"type":504,"tag":704,"props":1462,"children":1464},{"class":706,"line":1463},38,[1465,1470,1474,1479,1483,1487,1491,1495,1499,1503],{"type":504,"tag":704,"props":1466,"children":1467},{"style":753},[1468],{"type":510,"value":1469},"    onSettled",{"type":504,"tag":704,"props":1471,"children":1472},{"style":717},[1473],{"type":510,"value":1280},{"type":504,"tag":704,"props":1475,"children":1476},{"style":845},[1477],{"type":510,"value":1478},"_data",{"type":504,"tag":704,"props":1480,"children":1481},{"style":717},[1482],{"type":510,"value":853},{"type":504,"tag":704,"props":1484,"children":1485},{"style":845},[1486],{"type":510,"value":1285},{"type":504,"tag":704,"props":1488,"children":1489},{"style":717},[1490],{"type":510,"value":1290},{"type":504,"tag":704,"props":1492,"children":1493},{"style":845},[1494],{"type":510,"value":848},{"type":504,"tag":704,"props":1496,"children":1497},{"style":717},[1498],{"type":510,"value":909},{"type":504,"tag":704,"props":1500,"children":1501},{"style":711},[1502],{"type":510,"value":914},{"type":504,"tag":704,"props":1504,"children":1505},{"style":717},[1506],{"type":510,"value":919},{"type":504,"tag":704,"props":1508,"children":1510},{"class":706,"line":1509},39,[1511],{"type":504,"tag":704,"props":1512,"children":1513},{"style":1012},[1514],{"type":510,"value":1515},"      \u002F\u002F 6) 兜底最终一致性（特别是你没在 onSuccess 里覆盖时）\n",{"type":504,"tag":704,"props":1517,"children":1519},{"class":706,"line":1518},40,[1520,1524,1529,1533,1537],{"type":504,"tag":704,"props":1521,"children":1522},{"style":717},[1523],{"type":510,"value":1133},{"type":504,"tag":704,"props":1525,"children":1526},{"style":753},[1527],{"type":510,"value":1528},"invalidateQueries",{"type":504,"tag":704,"props":1530,"children":1531},{"style":717},[1532],{"type":510,"value":1039},{"type":504,"tag":704,"props":1534,"children":1535},{"style":728},[1536],{"type":510,"value":1044},{"type":504,"tag":704,"props":1538,"children":1539},{"style":717},[1540],{"type":510,"value":1049},{"type":504,"tag":704,"props":1542,"children":1544},{"class":706,"line":1543},41,[1545],{"type":504,"tag":704,"props":1546,"children":1547},{"style":717},[1548],{"type":510,"value":952},{"type":504,"tag":704,"props":1550,"children":1552},{"class":706,"line":1551},42,[1553],{"type":504,"tag":704,"props":1554,"children":1555},{"style":717},[1556],{"type":510,"value":1557},"  })\n",{"type":504,"tag":704,"props":1559,"children":1561},{"class":706,"line":1560},43,[1562],{"type":504,"tag":704,"props":1563,"children":1564},{"style":717},[1565],{"type":510,"value":1566},"}\n",{"type":504,"tag":512,"props":1568,"children":1569},{},[1570],{"type":504,"tag":516,"props":1571,"children":1572},{},[1573],{"type":510,"value":1574},"注意：上面只演示 detail。真实项目还要考虑 list、分页、聚合统计等“展示面”。",{"type":504,"tag":544,"props":1576,"children":1578},{"id":1577},"_3-你到底要更新哪些-cache更新范围设计",[1579],{"type":510,"value":1580},"3. 你到底要更新哪些 cache？（更新范围设计）",{"type":504,"tag":516,"props":1582,"children":1583},{},[1584],{"type":510,"value":1585},"乐观更新最容易做错的不是 API，而是“范围”。",{"type":504,"tag":686,"props":1587,"children":1589},{"id":1588},"_31-三种典型范围",[1590],{"type":510,"value":1591},"3.1 三种典型范围",{"type":504,"tag":1593,"props":1594,"children":1595},"ol",{},[1596],{"type":504,"tag":560,"props":1597,"children":1598},{},[1599],{"type":504,"tag":522,"props":1600,"children":1601},{},[1602],{"type":510,"value":1603},"只更新详情（detail only）",{"type":504,"tag":556,"props":1605,"children":1606},{},[1607,1612],{"type":504,"tag":560,"props":1608,"children":1609},{},[1610],{"type":510,"value":1611},"适用：页面只展示详情，或列表不需要立即同步",{"type":504,"tag":560,"props":1613,"children":1614},{},[1615,1617],{"type":510,"value":1616},"queryKey：",{"type":504,"tag":626,"props":1618,"children":1620},{"className":1619},[],[1621],{"type":510,"value":1622},"['todo', id]",{"type":504,"tag":1593,"props":1624,"children":1625},{"start":734},[1626],{"type":504,"tag":560,"props":1627,"children":1628},{},[1629],{"type":504,"tag":522,"props":1630,"children":1631},{},[1632],{"type":510,"value":1633},"同步更新列表 + 详情（list + detail）",{"type":504,"tag":556,"props":1635,"children":1636},{},[1637,1642],{"type":504,"tag":560,"props":1638,"children":1639},{},[1640],{"type":510,"value":1641},"适用：列表里有该项且用户会立刻看到变化（例如 toggle done \u002F like）",{"type":504,"tag":560,"props":1643,"children":1644},{},[1645,1647],{"type":510,"value":1646},"queryKey：\n",{"type":504,"tag":556,"props":1648,"children":1649},{},[1650,1660],{"type":504,"tag":560,"props":1651,"children":1652},{},[1653,1655],{"type":510,"value":1654},"detail：",{"type":504,"tag":626,"props":1656,"children":1658},{"className":1657},[],[1659],{"type":510,"value":1622},{"type":504,"tag":560,"props":1661,"children":1662},{},[1663,1665,1671],{"type":510,"value":1664},"list：",{"type":504,"tag":626,"props":1666,"children":1668},{"className":1667},[],[1669],{"type":510,"value":1670},"['todos', { filters... }]",{"type":510,"value":1672},"（注意：可能有多个不同 filters 的列表）",{"type":504,"tag":1593,"props":1674,"children":1675},{"start":744},[1676],{"type":504,"tag":560,"props":1677,"children":1678},{},[1679],{"type":504,"tag":522,"props":1680,"children":1681},{},[1682],{"type":510,"value":1683},"分页\u002F无限列表（paginated\u002Finfinite）",{"type":504,"tag":556,"props":1685,"children":1686},{},[1687,1692],{"type":504,"tag":560,"props":1688,"children":1689},{},[1690],{"type":510,"value":1691},"适用：长列表",{"type":504,"tag":560,"props":1693,"children":1694},{},[1695],{"type":510,"value":1696},"约束：不要 O(n) 全表重建；尽量做“局部 patch”",{"type":504,"tag":686,"props":1698,"children":1700},{"id":1699},"_32-经验法则",[1701],{"type":510,"value":1702},"3.2 经验法则",{"type":504,"tag":556,"props":1704,"children":1705},{},[1706,1711],{"type":504,"tag":560,"props":1707,"children":1708},{},[1709],{"type":510,"value":1710},"只同步“业务关键视图”的列表（命中率高的那几种 filters），不要把 invalidate\u002Fpatch 扩散到全站",{"type":504,"tag":560,"props":1712,"children":1713},{},[1714,1716,1721],{"type":510,"value":1715},"同一实体可能出现在多个列表：",{"type":504,"tag":522,"props":1717,"children":1718},{},[1719],{"type":510,"value":1720},"要么明确同步哪些列表，要么索性不做 list patch 只 invalidate",{"type":510,"value":1722},"（用正确性换一点体验）",{"type":504,"tag":544,"props":1724,"children":1726},{"id":1725},"_4-常见坑生产中最常见的事故来源",[1727],{"type":510,"value":1728},"4. 常见坑（生产中最常见的事故来源）",{"type":504,"tag":1593,"props":1730,"children":1731},{},[1732],{"type":504,"tag":560,"props":1733,"children":1734},{},[1735],{"type":504,"tag":522,"props":1736,"children":1737},{},[1738],{"type":510,"value":1739},"没 cancelQueries 导致竞态",{"type":504,"tag":556,"props":1741,"children":1742},{},[1743,1748],{"type":504,"tag":560,"props":1744,"children":1745},{},[1746],{"type":510,"value":1747},"场景：你乐观写入后，旧的 refetch 响应回来了，把缓存覆盖回旧值",{"type":504,"tag":560,"props":1749,"children":1750},{},[1751,1753,1758],{"type":510,"value":1752},"解决：",{"type":504,"tag":626,"props":1754,"children":1756},{"className":1755},[],[1757],{"type":510,"value":631},{"type":510,"value":1759}," 里 cancel 相关 queries",{"type":504,"tag":1593,"props":1761,"children":1762},{"start":734},[1763],{"type":504,"tag":560,"props":1764,"children":1765},{},[1766],{"type":504,"tag":522,"props":1767,"children":1768},{},[1769],{"type":510,"value":1770},"queryKey 设计不一致",{"type":504,"tag":556,"props":1772,"children":1773},{},[1774,1793],{"type":504,"tag":560,"props":1775,"children":1776},{},[1777,1779,1785,1787],{"type":510,"value":1778},"场景：你 patch 了 ",{"type":504,"tag":626,"props":1780,"children":1782},{"className":1781},[],[1783],{"type":510,"value":1784},"['todos']",{"type":510,"value":1786},"，但页面用的是 ",{"type":504,"tag":626,"props":1788,"children":1790},{"className":1789},[],[1791],{"type":510,"value":1792},"['todos', { page, keyword }]",{"type":504,"tag":560,"props":1794,"children":1795},{},[1796],{"type":510,"value":1797},"解决：统一 queryKey 工厂函数（建议集中在一个模块）",{"type":504,"tag":1593,"props":1799,"children":1800},{"start":744},[1801],{"type":504,"tag":560,"props":1802,"children":1803},{},[1804],{"type":504,"tag":522,"props":1805,"children":1806},{},[1807],{"type":510,"value":1808},"只更新列表不更新详情（或反过来）",{"type":504,"tag":556,"props":1810,"children":1811},{},[1812],{"type":504,"tag":560,"props":1813,"children":1814},{},[1815],{"type":510,"value":1816},"结果：同一实体在不同页面出现“多源真相”",{"type":504,"tag":1593,"props":1818,"children":1819},{"start":764},[1820],{"type":504,"tag":560,"props":1821,"children":1822},{},[1823],{"type":504,"tag":522,"props":1824,"children":1825},{},[1826],{"type":510,"value":1827},"创建（POST）没有临时 ID",{"type":504,"tag":556,"props":1829,"children":1830},{},[1831],{"type":504,"tag":560,"props":1832,"children":1833},{},[1834],{"type":510,"value":1835},"结果：列表里无法稳定替换、回滚困难、UI 抖动",{"type":504,"tag":1593,"props":1837,"children":1838},{"start":794},[1839],{"type":504,"tag":560,"props":1840,"children":1841},{},[1842],{"type":504,"tag":522,"props":1843,"children":1844},{},[1845],{"type":510,"value":1846},"并发 mutation（连点）+ 乱序返回",{"type":504,"tag":556,"props":1848,"children":1849},{},[1850,1855],{"type":504,"tag":560,"props":1851,"children":1852},{},[1853],{"type":510,"value":1854},"结果：后返回的响应覆盖前返回，状态来回跳",{"type":504,"tag":560,"props":1856,"children":1857},{},[1858,1860],{"type":510,"value":1859},"解法：\n",{"type":504,"tag":556,"props":1861,"children":1862},{},[1863,1868],{"type":504,"tag":560,"props":1864,"children":1865},{},[1866],{"type":510,"value":1867},"UI 上禁用按钮\u002F队列化",{"type":504,"tag":560,"props":1869,"children":1870},{},[1871,1873,1879],{"type":510,"value":1872},"或以 ",{"type":504,"tag":626,"props":1874,"children":1876},{"className":1875},[],[1877],{"type":510,"value":1878},"updatedAt\u002Fversion",{"type":510,"value":1880}," 做 reconcile（只接受更“新”的回包）",{"type":504,"tag":544,"props":1882,"children":1884},{"id":1883},"_5-常用落地模式你可以按业务选择",[1885],{"type":510,"value":1886},"5. 常用落地模式（你可以按业务选择）",{"type":504,"tag":686,"props":1888,"children":1890},{"id":1889},"pattern-ainvalidate-驱动正确性优先默认推荐",[1891],{"type":510,"value":1892},"Pattern A：Invalidate 驱动（正确性优先，默认推荐）",{"type":504,"tag":556,"props":1894,"children":1895},{},[1896,1908,1913],{"type":504,"tag":560,"props":1897,"children":1898},{},[1899,1901,1906],{"type":510,"value":1900},"mutation 成功 → ",{"type":504,"tag":626,"props":1902,"children":1904},{"className":1903},[],[1905],{"type":510,"value":1528},{"type":510,"value":1907}," → 让页面自动刷新",{"type":504,"tag":560,"props":1909,"children":1910},{},[1911],{"type":510,"value":1912},"优点：简单、正确性高",{"type":504,"tag":560,"props":1914,"children":1915},{},[1916],{"type":510,"value":1917},"缺点：交互反馈慢（依赖网络）",{"type":504,"tag":686,"props":1919,"children":1921},{"id":1920},"pattern-b标准乐观更新体验与正确性平衡",[1922],{"type":510,"value":1923},"Pattern B：标准乐观更新（体验与正确性平衡）",{"type":504,"tag":556,"props":1925,"children":1926},{},[1927,1937,1947,1957],{"type":504,"tag":560,"props":1928,"children":1929},{},[1930,1935],{"type":504,"tag":626,"props":1931,"children":1933},{"className":1932},[],[1934],{"type":510,"value":631},{"type":510,"value":1936},"：snapshot + setQueryData（局部 patch）",{"type":504,"tag":560,"props":1938,"children":1939},{},[1940,1945],{"type":504,"tag":626,"props":1941,"children":1943},{"className":1942},[],[1944],{"type":510,"value":660},{"type":510,"value":1946},"：rollback",{"type":504,"tag":560,"props":1948,"children":1949},{},[1950,1955],{"type":504,"tag":626,"props":1951,"children":1953},{"className":1952},[],[1954],{"type":510,"value":671},{"type":510,"value":1956},"：用服务端回包 reconcile",{"type":504,"tag":560,"props":1958,"children":1959},{},[1960,1965],{"type":504,"tag":626,"props":1961,"children":1963},{"className":1962},[],[1964],{"type":510,"value":682},{"type":510,"value":1966},"：invalidate 做最终一致性兜底",{"type":504,"tag":516,"props":1968,"children":1969},{},[1970],{"type":510,"value":1971},"适用：",{"type":504,"tag":556,"props":1973,"children":1974},{},[1975],{"type":504,"tag":560,"props":1976,"children":1977},{},[1978],{"type":510,"value":1979},"toggle、点赞、已读\u002F未读、轻量字段编辑",{"type":504,"tag":686,"props":1981,"children":1983},{"id":1982},"pattern-ccreatepost-临时-id-替换",[1984],{"type":510,"value":1985},"Pattern C：Create（POST）+ 临时 ID 替换",{"type":504,"tag":556,"props":1987,"children":1988},{},[1989,2026,2036],{"type":504,"tag":560,"props":1990,"children":1991},{},[1992,1997,1999],{"type":504,"tag":626,"props":1993,"children":1995},{"className":1994},[],[1996],{"type":510,"value":631},{"type":510,"value":1998},"：\n",{"type":504,"tag":556,"props":2000,"children":2001},{},[2002,2013],{"type":504,"tag":560,"props":2003,"children":2004},{},[2005,2007],{"type":510,"value":2006},"生成 ",{"type":504,"tag":626,"props":2008,"children":2010},{"className":2009},[],[2011],{"type":510,"value":2012},"tempId",{"type":504,"tag":560,"props":2014,"children":2015},{},[2016,2018,2024],{"type":510,"value":2017},"先把临时 item 插入 list（可标记 ",{"type":504,"tag":626,"props":2019,"children":2021},{"className":2020},[],[2022],{"type":510,"value":2023},"__optimistic: true",{"type":510,"value":2025},"）",{"type":504,"tag":560,"props":2027,"children":2028},{},[2029,2034],{"type":504,"tag":626,"props":2030,"children":2032},{"className":2031},[],[2033],{"type":510,"value":671},{"type":510,"value":2035},"：把 tempId 替换为服务端 id，并补全字段",{"type":504,"tag":560,"props":2037,"children":2038},{},[2039,2044],{"type":504,"tag":626,"props":2040,"children":2042},{"className":2041},[],[2043],{"type":510,"value":660},{"type":510,"value":2045},"：移除临时项 \u002F 回滚列表",{"type":504,"tag":686,"props":2047,"children":2049},{"id":2048},"pattern-ddeletedelete-undo可选",[2050],{"type":510,"value":2051},"Pattern D：Delete（DELETE）+ Undo（可选）",{"type":504,"tag":556,"props":2053,"children":2054},{},[2055,2065,2075],{"type":504,"tag":560,"props":2056,"children":2057},{},[2058,2063],{"type":504,"tag":626,"props":2059,"children":2061},{"className":2060},[],[2062],{"type":510,"value":631},{"type":510,"value":2064},"：从列表移除，并缓存被删项用于撤销",{"type":504,"tag":560,"props":2066,"children":2067},{},[2068,2073],{"type":504,"tag":626,"props":2069,"children":2071},{"className":2070},[],[2072],{"type":510,"value":660},{"type":510,"value":2074},"：插回",{"type":504,"tag":560,"props":2076,"children":2077},{},[2078,2083],{"type":504,"tag":626,"props":2079,"children":2081},{"className":2080},[],[2082],{"type":510,"value":682},{"type":510,"value":2084},"：invalidate 校准",{"type":504,"tag":544,"props":2086,"children":2088},{"id":2087},"_6-checklist",[2089],{"type":510,"value":2090},"6. Checklist",{"type":504,"tag":556,"props":2092,"children":2095},{"className":2093},[2094],"contains-task-list",[2096,2108,2131,2140,2149,2164,2179,2194,2203],{"type":504,"tag":560,"props":2097,"children":2100},{"className":2098},[2099],"task-list-item",[2101,2106],{"type":504,"tag":2102,"props":2103,"children":2105},"input",{"disabled":738,"type":2104},"checkbox",[],{"type":510,"value":2107}," 这次 mutation 影响哪些 queryKey？（list \u002F detail \u002F 聚合统计）",{"type":504,"tag":560,"props":2109,"children":2111},{"className":2110},[2099],[2112,2115,2117,2122,2124,2129],{"type":504,"tag":2102,"props":2113,"children":2114},{"disabled":738,"type":2104},[],{"type":510,"value":2116}," ",{"type":504,"tag":626,"props":2118,"children":2120},{"className":2119},[],[2121],{"type":510,"value":631},{"type":510,"value":2123}," 是否 ",{"type":504,"tag":626,"props":2125,"children":2127},{"className":2126},[],[2128],{"type":510,"value":1034},{"type":510,"value":2130}," 避免竞态覆盖？",{"type":504,"tag":560,"props":2132,"children":2134},{"className":2133},[2099],[2135,2138],{"type":504,"tag":2102,"props":2136,"children":2137},{"disabled":738,"type":2104},[],{"type":510,"value":2139}," 是否保存了可回滚的 snapshot（context）？",{"type":504,"tag":560,"props":2141,"children":2143},{"className":2142},[2099],[2144,2147],{"type":504,"tag":2102,"props":2145,"children":2146},{"disabled":738,"type":2104},[],{"type":510,"value":2148}," patch 是否不可变更新（不要直接 mutate 原对象）？",{"type":504,"tag":560,"props":2150,"children":2152},{"className":2151},[2099],[2153,2156,2157,2162],{"type":504,"tag":2102,"props":2154,"children":2155},{"disabled":738,"type":2104},[],{"type":510,"value":2116},{"type":504,"tag":626,"props":2158,"children":2160},{"className":2159},[],[2161],{"type":510,"value":660},{"type":510,"value":2163}," 回滚是否能恢复到“用户可理解”的状态？",{"type":504,"tag":560,"props":2165,"children":2167},{"className":2166},[2099],[2168,2171,2172,2177],{"type":504,"tag":2102,"props":2169,"children":2170},{"disabled":738,"type":2104},[],{"type":510,"value":2116},{"type":504,"tag":626,"props":2173,"children":2175},{"className":2174},[],[2176],{"type":510,"value":671},{"type":510,"value":2178}," 是否用服务端回包 reconcile（权限\u002F校验\u002F补全字段）？",{"type":504,"tag":560,"props":2180,"children":2182},{"className":2181},[2099],[2183,2186,2187,2192],{"type":504,"tag":2102,"props":2184,"children":2185},{"disabled":738,"type":2104},[],{"type":510,"value":2116},{"type":504,"tag":626,"props":2188,"children":2190},{"className":2189},[],[2191],{"type":510,"value":682},{"type":510,"value":2193}," 是否有 invalidate 作为最终一致性兜底？粒度是否可控？",{"type":504,"tag":560,"props":2195,"children":2197},{"className":2196},[2099],[2198,2201],{"type":504,"tag":2102,"props":2199,"children":2200},{"disabled":738,"type":2104},[],{"type":510,"value":2202}," 是否处理并发 mutation（连点\u002F乱序返回）？",{"type":504,"tag":560,"props":2204,"children":2206},{"className":2205},[2099],[2207,2210],{"type":504,"tag":2102,"props":2208,"children":2209},{"disabled":738,"type":2104},[],{"type":510,"value":2211}," 是否避免“多源真相”（store\u002F组件 state\u002FURL\u002Fquery cache 重复存一份）？",{"type":504,"tag":544,"props":2213,"children":2215},{"id":2214},"关联阅读",[2216],{"type":510,"value":2214},{"type":504,"tag":556,"props":2218,"children":2219},{},[2220,2235,2249,2263],{"type":504,"tag":560,"props":2221,"children":2222},{},[2223,2225],{"type":510,"value":2224},"状态分层（source of truth）：",{"type":504,"tag":2226,"props":2227,"children":2229},"a",{"href":2228},".\u002F01-state-layering-ui-server-url-global.md",[2230],{"type":504,"tag":626,"props":2231,"children":2233},{"className":2232},[],[2234],{"type":510,"value":2228},{"type":504,"tag":560,"props":2236,"children":2237},{},[2238,2240],{"type":510,"value":2239},"Query 缓存与失效（invalidate 基础）：",{"type":504,"tag":2226,"props":2241,"children":2243},{"href":2242},".\u002F02-tanstack-query-caching-and-invalidation.md",[2244],{"type":504,"tag":626,"props":2245,"children":2247},{"className":2246},[],[2248],{"type":510,"value":2242},{"type":504,"tag":560,"props":2250,"children":2251},{},[2252,2254],{"type":510,"value":2253},"Transition（输入与结果区解耦，降低“必须乐观”的压力）：",{"type":504,"tag":2226,"props":2255,"children":2257},{"href":2256},"..\u002F09-react-18-in-practice\u002F03-transition-useTransition-useDeferredValue.md",[2258],{"type":504,"tag":626,"props":2259,"children":2261},{"className":2260},[],[2262],{"type":510,"value":2256},{"type":504,"tag":560,"props":2264,"children":2265},{},[2266,2268],{"type":510,"value":2267},"Suspense 与加载边界：",{"type":504,"tag":2226,"props":2269,"children":2271},{"href":2270},"..\u002F09-react-18-in-practice\u002F04-suspense-loading-boundary-design.md",[2272],{"type":504,"tag":626,"props":2273,"children":2275},{"className":2274},[],[2276],{"type":510,"value":2270},{"type":504,"tag":2278,"props":2279,"children":2280},"style",{},[2281],{"type":510,"value":2282},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":698,"searchDepth":734,"depth":734,"links":2284},[2285,2286,2289,2293,2294,2300,2301],{"id":546,"depth":734,"text":549},{"id":610,"depth":734,"text":613,"children":2287},[2288],{"id":688,"depth":744,"text":691},{"id":1577,"depth":734,"text":1580,"children":2290},[2291,2292],{"id":1588,"depth":744,"text":1591},{"id":1699,"depth":744,"text":1702},{"id":1725,"depth":734,"text":1728},{"id":1883,"depth":734,"text":1886,"children":2295},[2296,2297,2298,2299],{"id":1889,"depth":744,"text":1892},{"id":1920,"depth":744,"text":1923},{"id":1982,"depth":744,"text":1985},{"id":2048,"depth":744,"text":2051},{"id":2087,"depth":734,"text":2090},{"id":2214,"depth":734,"text":2214},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":2303},"\u003Cpath fill=\"currentColor\" d=\"m10.828 12l4.95 4.95l-1.414 1.415L8 12l6.364-6.364l1.414 1.414z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":2305},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":2307},"\u003Cpath fill=\"currentColor\" d=\"m13.172 12l-4.95-4.95l1.414-1.413L16 12l-6.364 6.364l-1.414-1.415z\"\u002F>",{"left":4,"top":4,"width":2309,"height":2309,"rotate":4,"vFlip":6,"hFlip":6,"body":2310},1024,"\u003Cpath fill=\"currentColor\" d=\"M768 832a128 128 0 0 1-128 128H192A128 128 0 0 1 64 832V384a128 128 0 0 1 128-128v64a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64z\"\u002F>\u003Cpath fill=\"currentColor\" d=\"M384 128a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64zm0-64h448a128 128 0 0 1 128 128v448a128 128 0 0 1-128 128H384a128 128 0 0 1-128-128V192A128 128 0 0 1 384 64\"\u002F>"]