[{"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\u002F04-form-architecture-react-hook-form-validation-performance-testability":494,"i-ri:arrow-left-s-line":1763,"i-ri:menu-2-line":1765,"i-ri:arrow-right-s-line":1767},{"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":395,"path":395,"title":396,"updatedAt":21,"meta":498,"body":500,"toc":1727},"bm90ZTpub3Rlcy9yZWFjdC8xMS1kYXRhLWxheWVyLzA0LWZvcm0tYXJjaGl0ZWN0dXJlLXJlYWN0LWhvb2stZm9ybS12YWxpZGF0aW9uLXBlcmZvcm1hbmNlLXRlc3RhYmlsaXR5Lm1k","note","notes\u002Freact\u002F11-data-layer\u002F04-form-architecture-react-hook-form-validation-performance-testability.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":396,"updatedAt":21,"lastUpdated":21},"react\u002F11-data-layer\u002F04-form-architecture-react-hook-form-validation-performance-testability",{"type":501,"children":502},"root",[503,511,529,536,541,571,576,594,600,607,637,642,655,660,668,674,687,691,699,703,711,717,722,771,779,791,803,808,826,832,837,912,917,931,937,943,954,975,987,1000,1012,1031,1039,1045,1050,1054,1067,1073,1079,1097,1102,1115,1121,1248,1254,1260,1278,1284,1302,1308,1314,1327,1333,1373,1379,1409,1415,1420,1426,1449,1455,1476,1482,1550,1558,1564,1648,1653],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"表单架构react-hook-form-的受控非受控校验性能与可测试性架构师视角",[509],{"type":510,"value":396},"text",{"type":504,"tag":512,"props":513,"children":514},"blockquote",{},[515],{"type":504,"tag":516,"props":517,"children":518},"p",{},[519,521,527],{"type":510,"value":520},"一句话结论：把表单当成“高频 UI state + 低频提交事件”的系统来设计——用 React Hook Form（默认非受控）把输入期的渲染成本压到最低，同时用",{"type":504,"tag":522,"props":523,"children":524},"strong",{},[525],{"type":510,"value":526},"分层的校验策略",{"type":510,"value":528},"与**清晰的状态边界（UI \u002F Form \u002F Server \u002F URL）**保证一致性、可测试性与可访问性。",{"type":504,"tag":530,"props":531,"children":533},"h2",{"id":532},"_0-为什么表单会成为架构问题",[534],{"type":510,"value":535},"0. 为什么表单会成为架构问题？",{"type":504,"tag":516,"props":537,"children":538},{},[539],{"type":510,"value":540},"表单一旦变大（字段多、联动多、校验复杂、可复用组件多），常见症状是：",{"type":504,"tag":542,"props":543,"children":544},"ul",{},[545,551,556,561,566],{"type":504,"tag":546,"props":547,"children":548},"li",{},[549],{"type":510,"value":550},"输入卡顿：每个字符导致大面积 re-render",{"type":504,"tag":546,"props":552,"children":553},{},[554],{"type":510,"value":555},"校验混乱：前端规则、后端规则、异步校验各写一套",{"type":504,"tag":546,"props":557,"children":558},{},[559],{"type":510,"value":560},"状态打架：表单值、URL 条件、列表缓存彼此不同步",{"type":504,"tag":546,"props":562,"children":563},{},[564],{"type":510,"value":565},"可测性差：只能 E2E 点点点，边界很难覆盖",{"type":504,"tag":546,"props":567,"children":568},{},[569],{"type":510,"value":570},"a11y 缺失：错误不可读、焦点不可预期",{"type":504,"tag":516,"props":572,"children":573},{},[574],{"type":510,"value":575},"这篇的目标不是教 API，而是给你一套“默认推荐”的工程语义：",{"type":504,"tag":542,"props":577,"children":578},{},[579,584,589],{"type":504,"tag":546,"props":580,"children":581},{},[582],{"type":510,"value":583},"输入期：性能优先（少渲染）",{"type":504,"tag":546,"props":585,"children":586},{},[587],{"type":510,"value":588},"提交期：一致性优先（与服务端\u002F缓存对齐）",{"type":504,"tag":546,"props":590,"children":591},{},[592],{"type":510,"value":593},"展示期：可访问性优先（语义清晰、错误可被理解）",{"type":504,"tag":530,"props":595,"children":597},{"id":596},"_1-受控-vs-非受控source-of-truth-在哪",[598],{"type":510,"value":599},"1. 受控 vs 非受控：source of truth 在哪？",{"type":504,"tag":601,"props":602,"children":604},"h3",{"id":603},"_11-受控controlled输入",[605],{"type":510,"value":606},"1.1 受控（Controlled）输入",{"type":504,"tag":542,"props":608,"children":609},{},[610,615],{"type":504,"tag":546,"props":611,"children":612},{},[613],{"type":510,"value":614},"真相在 React state",{"type":504,"tag":546,"props":616,"children":617},{},[618,620,627,629,635],{"type":510,"value":619},"每次 ",{"type":504,"tag":621,"props":622,"children":624},"code",{"className":623},[],[625],{"type":510,"value":626},"onChange",{"type":510,"value":628}," → ",{"type":504,"tag":621,"props":630,"children":632},{"className":631},[],[633],{"type":510,"value":634},"setState",{"type":510,"value":636}," → re-render",{"type":504,"tag":516,"props":638,"children":639},{},[640],{"type":510,"value":641},"优点：",{"type":504,"tag":542,"props":643,"children":644},{},[645,650],{"type":504,"tag":546,"props":646,"children":647},{},[648],{"type":510,"value":649},"联动和派生 UI 容易做",{"type":504,"tag":546,"props":651,"children":652},{},[653],{"type":510,"value":654},"值完全可控",{"type":504,"tag":516,"props":656,"children":657},{},[658],{"type":510,"value":659},"代价：",{"type":504,"tag":542,"props":661,"children":662},{},[663],{"type":504,"tag":546,"props":664,"children":665},{},[666],{"type":510,"value":667},"大表单很容易把每个字符变成“全树更新”",{"type":504,"tag":601,"props":669,"children":671},{"id":670},"_12-非受控uncontrolled输入",[672],{"type":510,"value":673},"1.2 非受控（Uncontrolled）输入",{"type":504,"tag":542,"props":675,"children":676},{},[677,682],{"type":504,"tag":546,"props":678,"children":679},{},[680],{"type":510,"value":681},"真相在 DOM input value（通过 ref 读取\u002F订阅）",{"type":504,"tag":546,"props":683,"children":684},{},[685],{"type":510,"value":686},"输入本身不必触发组件重渲染",{"type":504,"tag":516,"props":688,"children":689},{},[690],{"type":510,"value":641},{"type":504,"tag":542,"props":692,"children":693},{},[694],{"type":504,"tag":546,"props":695,"children":696},{},[697],{"type":510,"value":698},"性能友好（尤其是大量字段）",{"type":504,"tag":516,"props":700,"children":701},{},[702],{"type":510,"value":659},{"type":504,"tag":542,"props":704,"children":705},{},[706],{"type":504,"tag":546,"props":707,"children":708},{},[709],{"type":510,"value":710},"需要更明确“何时读取值、何时提交值”",{"type":504,"tag":601,"props":712,"children":714},{"id":713},"_13-react-hook-form-的选择默认非受控-精准订阅",[715],{"type":510,"value":716},"1.3 React Hook Form 的选择：默认非受控 + 精准订阅",{"type":504,"tag":516,"props":718,"children":719},{},[720],{"type":510,"value":721},"React Hook Form（RHF）默认走非受控路径：",{"type":504,"tag":542,"props":723,"children":724},{},[725,736,752],{"type":504,"tag":546,"props":726,"children":727},{},[728,734],{"type":504,"tag":621,"props":729,"children":731},{"className":730},[],[732],{"type":510,"value":733},"register",{"type":510,"value":735},"：把 input 接入表单系统",{"type":504,"tag":546,"props":737,"children":738},{},[739,745,747],{"type":504,"tag":621,"props":740,"children":742},{"className":741},[],[743],{"type":510,"value":744},"formState",{"type":510,"value":746},"：包含 errors\u002Fdirty\u002FisSubmitting 等，但",{"type":504,"tag":522,"props":748,"children":749},{},[750],{"type":510,"value":751},"不要全量订阅",{"type":504,"tag":546,"props":753,"children":754},{},[755,761,763,769],{"type":504,"tag":621,"props":756,"children":758},{"className":757},[],[759],{"type":510,"value":760},"useFormState",{"type":510,"value":762}," \u002F ",{"type":504,"tag":621,"props":764,"children":766},{"className":765},[],[767],{"type":510,"value":768},"useWatch",{"type":510,"value":770},"：做“局部订阅”，避免无谓渲染",{"type":504,"tag":512,"props":772,"children":773},{},[774],{"type":504,"tag":516,"props":775,"children":776},{},[777],{"type":510,"value":778},"经验：表单性能的关键不是“用不用 RHF”，而是你有没有把订阅面控制住。",{"type":504,"tag":601,"props":780,"children":782},{"id":781},"_14-何时用-controller",[783,785],{"type":510,"value":784},"1.4 何时用 ",{"type":504,"tag":621,"props":786,"children":788},{"className":787},[],[789],{"type":510,"value":790},"Controller",{"type":504,"tag":516,"props":792,"children":793},{},[794,796,801],{"type":510,"value":795},"当你接入的组件天然是受控组件（很多 UI 库都是），才用 ",{"type":504,"tag":621,"props":797,"children":799},{"className":798},[],[800],{"type":510,"value":790},{"type":510,"value":802}," 做桥接。",{"type":504,"tag":516,"props":804,"children":805},{},[806],{"type":510,"value":807},"工程约束：",{"type":504,"tag":542,"props":809,"children":810},{},[811,821],{"type":504,"tag":546,"props":812,"children":813},{},[814,816],{"type":510,"value":815},"只在必要字段用 ",{"type":504,"tag":621,"props":817,"children":819},{"className":818},[],[820],{"type":510,"value":790},{"type":504,"tag":546,"props":822,"children":823},{},[824],{"type":510,"value":825},"昂贵的组件隔离渲染边界（拆分字段组件 + memo + 局部订阅）",{"type":504,"tag":530,"props":827,"children":829},{"id":828},"_2-表单的状态分层ui-form-server-url-怎么切",[830],{"type":510,"value":831},"2. 表单的状态分层：UI \u002F Form \u002F Server \u002F URL 怎么切？",{"type":504,"tag":516,"props":833,"children":834},{},[835],{"type":510,"value":836},"把表单放回“状态分层”的大系统里：",{"type":504,"tag":542,"props":838,"children":839},{},[840,858,876,894],{"type":504,"tag":546,"props":841,"children":842},{},[843,848,850],{"type":504,"tag":522,"props":844,"children":845},{},[846],{"type":510,"value":847},"UI State",{"type":510,"value":849},"（交互体验）\n",{"type":504,"tag":542,"props":851,"children":852},{},[853],{"type":504,"tag":546,"props":854,"children":855},{},[856],{"type":510,"value":857},"展开\u002F收起、step、局部提示、弹窗开关",{"type":504,"tag":546,"props":859,"children":860},{},[861,866,868],{"type":504,"tag":522,"props":862,"children":863},{},[864],{"type":510,"value":865},"Form State（草稿）",{"type":510,"value":867},"（RHF 管理）\n",{"type":504,"tag":542,"props":869,"children":870},{},[871],{"type":504,"tag":546,"props":872,"children":873},{},[874],{"type":510,"value":875},"values \u002F touched \u002F dirty \u002F errors \u002F isSubmitting",{"type":504,"tag":546,"props":877,"children":878},{},[879,884,886],{"type":504,"tag":522,"props":880,"children":881},{},[882],{"type":510,"value":883},"Server State",{"type":510,"value":885},"（服务端数据与一致性）\n",{"type":504,"tag":542,"props":887,"children":888},{},[889],{"type":504,"tag":546,"props":890,"children":891},{},[892],{"type":510,"value":893},"提交成功后的实体、列表\u002F详情缓存（TanStack Query 或 Next 缓存）",{"type":504,"tag":546,"props":895,"children":896},{},[897,902,904],{"type":504,"tag":522,"props":898,"children":899},{},[900],{"type":510,"value":901},"URL State",{"type":510,"value":903},"（可分享\u002F可回放的条件）\n",{"type":504,"tag":542,"props":905,"children":906},{},[907],{"type":504,"tag":546,"props":908,"children":909},{},[910],{"type":510,"value":911},"筛选\u002F分页\u002F排序等",{"type":504,"tag":516,"props":913,"children":914},{},[915],{"type":510,"value":916},"关键约束：",{"type":504,"tag":918,"props":919,"children":920},"ol",{},[921,926],{"type":504,"tag":546,"props":922,"children":923},{},[924],{"type":510,"value":925},"表单草稿（Form state）不等于服务端真相（Server state）",{"type":504,"tag":546,"props":927,"children":928},{},[929],{"type":510,"value":930},"筛选表单的“条件真相”应该在 URL（否则 back\u002Fforward 不可回放）",{"type":504,"tag":530,"props":932,"children":934},{"id":933},"_3-校验策略输入期提示-vs-提交期一致性",[935],{"type":510,"value":936},"3. 校验策略：输入期提示 vs 提交期一致性",{"type":504,"tag":601,"props":938,"children":940},{"id":939},"_31-推荐的三层校验",[941],{"type":510,"value":942},"3.1 推荐的三层校验",{"type":504,"tag":918,"props":944,"children":945},{},[946],{"type":504,"tag":546,"props":947,"children":948},{},[949],{"type":504,"tag":522,"props":950,"children":951},{},[952],{"type":510,"value":953},"输入期（局部、轻量）",{"type":504,"tag":542,"props":955,"children":956},{},[957,962],{"type":504,"tag":546,"props":958,"children":959},{},[960],{"type":510,"value":961},"required \u002F minLength \u002F pattern 等",{"type":504,"tag":546,"props":963,"children":964},{},[965,967,973],{"type":510,"value":966},"触发策略：优先 ",{"type":504,"tag":621,"props":968,"children":970},{"className":969},[],[971],{"type":510,"value":972},"onBlur",{"type":510,"value":974},"，不要每个字符都同步报错",{"type":504,"tag":918,"props":976,"children":978},{"start":977},2,[979],{"type":504,"tag":546,"props":980,"children":981},{},[982],{"type":504,"tag":522,"props":983,"children":984},{},[985],{"type":510,"value":986},"提交期（全量、可复用）",{"type":504,"tag":542,"props":988,"children":989},{},[990,995],{"type":504,"tag":546,"props":991,"children":992},{},[993],{"type":510,"value":994},"用 schema（Zod\u002FYup\u002FValibot）+ resolver 统一表达",{"type":504,"tag":546,"props":996,"children":997},{},[998],{"type":510,"value":999},"统一错误文案与 i18n",{"type":504,"tag":918,"props":1001,"children":1003},{"start":1002},3,[1004],{"type":504,"tag":546,"props":1005,"children":1006},{},[1007],{"type":504,"tag":522,"props":1008,"children":1009},{},[1010],{"type":510,"value":1011},"服务端校验（最终权威）",{"type":504,"tag":542,"props":1013,"children":1014},{},[1015,1026],{"type":504,"tag":546,"props":1016,"children":1017},{},[1018,1020],{"type":510,"value":1019},"HTTP 400\u002F422 字段错误映射到 RHF：",{"type":504,"tag":621,"props":1021,"children":1023},{"className":1022},[],[1024],{"type":510,"value":1025},"setError('field', { message })",{"type":504,"tag":546,"props":1027,"children":1028},{},[1029],{"type":510,"value":1030},"表单级错误（难定位字段） → error summary",{"type":504,"tag":512,"props":1032,"children":1033},{},[1034],{"type":504,"tag":516,"props":1035,"children":1036},{},[1037],{"type":510,"value":1038},"经验：前端校验解决“及时反馈与减少无效提交”，后端校验解决“最终正确性与安全边界”。",{"type":504,"tag":601,"props":1040,"children":1042},{"id":1041},"_32-异步校验async-validation",[1043],{"type":510,"value":1044},"3.2 异步校验（Async validation）",{"type":504,"tag":516,"props":1046,"children":1047},{},[1048],{"type":510,"value":1049},"典型：用户名是否可用、优惠码校验。",{"type":504,"tag":516,"props":1051,"children":1052},{},[1053],{"type":510,"value":807},{"type":504,"tag":542,"props":1055,"children":1056},{},[1057,1062],{"type":504,"tag":546,"props":1058,"children":1059},{},[1060],{"type":510,"value":1061},"防抖\u002F取消（避免请求风暴）",{"type":504,"tag":546,"props":1063,"children":1064},{},[1065],{"type":510,"value":1066},"不要让异步校验破坏输入流畅（必要时配合 Transition 或延后到 blur）",{"type":504,"tag":530,"props":1068,"children":1070},{"id":1069},"_4-性能让表单成为高频交互但低成本渲染的系统",[1071],{"type":510,"value":1072},"4. 性能：让表单成为“高频交互但低成本渲染”的系统",{"type":504,"tag":601,"props":1074,"children":1076},{"id":1075},"_41-性能目标你要能验证",[1077],{"type":510,"value":1078},"4.1 性能目标（你要能验证）",{"type":504,"tag":542,"props":1080,"children":1081},{},[1082,1087,1092],{"type":504,"tag":546,"props":1083,"children":1084},{},[1085],{"type":510,"value":1086},"输入不掉帧（INP 不被表单拖垮）",{"type":504,"tag":546,"props":1088,"children":1089},{},[1090],{"type":510,"value":1091},"联动不卡顿",{"type":504,"tag":546,"props":1093,"children":1094},{},[1095],{"type":510,"value":1096},"提交按钮状态稳定（避免抖动\u002F重复提交）",{"type":504,"tag":516,"props":1098,"children":1099},{},[1100],{"type":510,"value":1101},"开发期验证手段：",{"type":504,"tag":542,"props":1103,"children":1104},{},[1105,1110],{"type":504,"tag":546,"props":1106,"children":1107},{},[1108],{"type":510,"value":1109},"React Profiler 看 re-render 范围",{"type":504,"tag":546,"props":1111,"children":1112},{},[1113],{"type":510,"value":1114},"浏览器 Performance 看长任务",{"type":504,"tag":601,"props":1116,"children":1118},{"id":1117},"_42-rhf-的关键性能策略",[1119],{"type":510,"value":1120},"4.2 RHF 的关键性能策略",{"type":504,"tag":542,"props":1122,"children":1123},{},[1124,1167,1197,1214],{"type":504,"tag":546,"props":1125,"children":1126},{},[1127,1132,1134],{"type":504,"tag":522,"props":1128,"children":1129},{},[1130],{"type":510,"value":1131},"减少订阅面",{"type":510,"value":1133},"：",{"type":504,"tag":542,"props":1135,"children":1136},{},[1137,1147],{"type":504,"tag":546,"props":1138,"children":1139},{},[1140,1142],{"type":510,"value":1141},"不要在顶层组件全量读取 ",{"type":504,"tag":621,"props":1143,"children":1145},{"className":1144},[],[1146],{"type":510,"value":744},{"type":504,"tag":546,"props":1148,"children":1149},{},[1150,1152,1158,1159,1165],{"type":510,"value":1151},"使用 ",{"type":504,"tag":621,"props":1153,"children":1155},{"className":1154},[],[1156],{"type":510,"value":1157},"useFormState({ control, name })",{"type":510,"value":762},{"type":504,"tag":621,"props":1160,"children":1162},{"className":1161},[],[1163],{"type":510,"value":1164},"useWatch({ name })",{"type":510,"value":1166}," 做局部订阅",{"type":504,"tag":546,"props":1168,"children":1169},{},[1170,1175,1176],{"type":504,"tag":522,"props":1171,"children":1172},{},[1173],{"type":510,"value":1174},"隔离昂贵字段",{"type":510,"value":1133},{"type":504,"tag":542,"props":1177,"children":1178},{},[1179,1184],{"type":504,"tag":546,"props":1180,"children":1181},{},[1182],{"type":510,"value":1183},"一个字段一个组件（Field Component）",{"type":504,"tag":546,"props":1185,"children":1186},{},[1187,1189,1195],{"type":510,"value":1188},"用 ",{"type":504,"tag":621,"props":1190,"children":1192},{"className":1191},[],[1193],{"type":510,"value":1194},"memo",{"type":510,"value":1196}," + 局部订阅",{"type":504,"tag":546,"props":1198,"children":1199},{},[1200,1205,1206],{"type":504,"tag":522,"props":1201,"children":1202},{},[1203],{"type":510,"value":1204},"FieldArray（动态字段）",{"type":510,"value":1133},{"type":504,"tag":542,"props":1207,"children":1208},{},[1209],{"type":504,"tag":546,"props":1210,"children":1211},{},[1212],{"type":510,"value":1213},"大量动态行时考虑分页\u002F虚拟列表",{"type":504,"tag":546,"props":1215,"children":1216},{},[1217,1222,1223],{"type":504,"tag":522,"props":1218,"children":1219},{},[1220],{"type":510,"value":1221},"初始化\u002F重置策略",{"type":510,"value":1133},{"type":504,"tag":542,"props":1224,"children":1225},{},[1226,1237],{"type":504,"tag":546,"props":1227,"children":1228},{},[1229,1231],{"type":510,"value":1230},"初始值来自服务端时，避免每次 render 都 ",{"type":504,"tag":621,"props":1232,"children":1234},{"className":1233},[],[1235],{"type":510,"value":1236},"reset",{"type":504,"tag":546,"props":1238,"children":1239},{},[1240,1242],{"type":510,"value":1241},"建议：只在数据 ready 时执行一次 ",{"type":504,"tag":621,"props":1243,"children":1245},{"className":1244},[],[1246],{"type":510,"value":1247},"reset(defaultValues)",{"type":504,"tag":530,"props":1249,"children":1251},{"id":1250},"_5-可测试性让表单能被业务语义测试",[1252],{"type":510,"value":1253},"5. 可测试性：让表单能被“业务语义”测试",{"type":504,"tag":601,"props":1255,"children":1257},{"id":1256},"_51-测试分层建议",[1258],{"type":510,"value":1259},"5.1 测试分层（建议）",{"type":504,"tag":542,"props":1261,"children":1262},{},[1263,1268,1273],{"type":504,"tag":546,"props":1264,"children":1265},{},[1266],{"type":510,"value":1267},"单元测试：schema\u002F格式化\u002F映射函数（纯函数）",{"type":504,"tag":546,"props":1269,"children":1270},{},[1271],{"type":510,"value":1272},"组件集成测试：React Testing Library + user-event",{"type":504,"tag":546,"props":1274,"children":1275},{},[1276],{"type":510,"value":1277},"E2E：关键路径（注册\u002F支付\u002F创建工单）",{"type":504,"tag":601,"props":1279,"children":1281},{"id":1280},"_52-可测试性的架构要点",[1282],{"type":510,"value":1283},"5.2 可测试性的架构要点",{"type":504,"tag":542,"props":1285,"children":1286},{},[1287,1292,1297],{"type":504,"tag":546,"props":1288,"children":1289},{},[1290],{"type":510,"value":1291},"提交副作用抽成可注入依赖（API client \u002F mutationFn）",{"type":504,"tag":546,"props":1293,"children":1294},{},[1295],{"type":510,"value":1296},"校验逻辑集中在 schema（不要散落 JSX）",{"type":504,"tag":546,"props":1298,"children":1299},{},[1300],{"type":510,"value":1301},"测试尽量用语义化查询（role\u002Flabel\u002Fname），间接提升可访问性",{"type":504,"tag":530,"props":1303,"children":1305},{"id":1304},"_6-与-server-statetanstack-query协作提交失效与错误映射",[1306],{"type":510,"value":1307},"6. 与 Server State（TanStack Query）协作：提交、失效与错误映射",{"type":504,"tag":601,"props":1309,"children":1311},{"id":1310},"_61-表单提交的定位",[1312],{"type":510,"value":1313},"6.1 表单提交的定位",{"type":504,"tag":542,"props":1315,"children":1316},{},[1317,1322],{"type":504,"tag":546,"props":1318,"children":1319},{},[1320],{"type":510,"value":1321},"表单草稿不是 Server State",{"type":504,"tag":546,"props":1323,"children":1324},{},[1325],{"type":510,"value":1326},"提交成功后的实体更新、列表刷新才进入 Server State",{"type":504,"tag":601,"props":1328,"children":1330},{"id":1329},"_62-常见集成模式",[1331],{"type":510,"value":1332},"6.2 常见集成模式",{"type":504,"tag":542,"props":1334,"children":1335},{},[1336,1347],{"type":504,"tag":546,"props":1337,"children":1338},{},[1339,1345],{"type":504,"tag":621,"props":1340,"children":1342},{"className":1341},[],[1343],{"type":510,"value":1344},"useMutation",{"type":510,"value":1346}," 提交",{"type":504,"tag":546,"props":1348,"children":1349},{},[1350,1352],{"type":510,"value":1351},"成功后：\n",{"type":504,"tag":542,"props":1353,"children":1354},{},[1355,1368],{"type":504,"tag":546,"props":1356,"children":1357},{},[1358,1360,1366],{"type":510,"value":1359},"默认策略：",{"type":504,"tag":621,"props":1361,"children":1363},{"className":1362},[],[1364],{"type":510,"value":1365},"invalidateQueries",{"type":510,"value":1367},"（正确性高）",{"type":504,"tag":546,"props":1369,"children":1370},{},[1371],{"type":510,"value":1372},"进阶策略：乐观更新（见下一篇）",{"type":504,"tag":601,"props":1374,"children":1376},{"id":1375},"_63-把服务端字段错误映射回表单",[1377],{"type":510,"value":1378},"6.3 把服务端字段错误映射回表单",{"type":504,"tag":542,"props":1380,"children":1381},{},[1382,1387,1404],{"type":504,"tag":546,"props":1383,"children":1384},{},[1385],{"type":510,"value":1386},"服务端返回 422（字段校验失败）",{"type":504,"tag":546,"props":1388,"children":1389},{},[1390,1392],{"type":510,"value":1391},"前端将错误映射到字段：\n",{"type":504,"tag":542,"props":1393,"children":1394},{},[1395],{"type":504,"tag":546,"props":1396,"children":1397},{},[1398],{"type":504,"tag":621,"props":1399,"children":1401},{"className":1400},[],[1402],{"type":510,"value":1403},"setError('email', { message: '邮箱已存在' })",{"type":504,"tag":546,"props":1405,"children":1406},{},[1407],{"type":510,"value":1408},"同时提供表单级错误 summary，避免用户找不到问题",{"type":504,"tag":530,"props":1410,"children":1412},{"id":1411},"_7-与-url-state-协作筛选表单可分享链接与回退回放",[1413],{"type":510,"value":1414},"7. 与 URL State 协作：筛选表单、可分享链接与回退回放",{"type":504,"tag":516,"props":1416,"children":1417},{},[1418],{"type":510,"value":1419},"筛选表单是最容易“状态打架”的地方。",{"type":504,"tag":601,"props":1421,"children":1423},{"id":1422},"_71-推荐模式url-是条件真相表单是编辑器",[1424],{"type":510,"value":1425},"7.1 推荐模式：URL 是条件真相，表单是编辑器",{"type":504,"tag":542,"props":1427,"children":1428},{},[1429,1434,1439,1444],{"type":504,"tag":546,"props":1430,"children":1431},{},[1432],{"type":510,"value":1433},"URL → 解析为默认值（初始化\u002F回放）",{"type":504,"tag":546,"props":1435,"children":1436},{},[1437],{"type":510,"value":1438},"用户编辑 → 暂存在 RHF（草稿）",{"type":504,"tag":546,"props":1440,"children":1441},{},[1442],{"type":510,"value":1443},"用户点击“应用筛选” → 写回 URL（replace\u002Fpush 视业务）",{"type":504,"tag":546,"props":1445,"children":1446},{},[1447],{"type":510,"value":1448},"URL 变化 → 派生 queryKey → 列表数据刷新\u002F命中缓存",{"type":504,"tag":601,"props":1450,"children":1452},{"id":1451},"_72-常见坑",[1453],{"type":510,"value":1454},"7.2 常见坑",{"type":504,"tag":542,"props":1456,"children":1457},{},[1458,1471],{"type":504,"tag":546,"props":1459,"children":1460},{},[1461,1463,1469],{"type":510,"value":1462},"双向实时同步导致循环：",{"type":504,"tag":621,"props":1464,"children":1466},{"className":1465},[],[1467],{"type":510,"value":1468},"watch",{"type":510,"value":1470}," → 写 URL → 触发 reset → …",{"type":504,"tag":546,"props":1472,"children":1473},{},[1474],{"type":510,"value":1475},"每个字符都写 URL：back\u002Fforward 变成“逐字符回放”灾难",{"type":504,"tag":530,"props":1477,"children":1479},{"id":1478},"_8-可访问性a11y错误与焦点必须可预期",[1480],{"type":510,"value":1481},"8. 可访问性（A11y）：错误与焦点必须可预期",{"type":504,"tag":542,"props":1483,"children":1484},{},[1485,1504,1532],{"type":504,"tag":546,"props":1486,"children":1487},{},[1488,1494,1496,1502],{"type":504,"tag":621,"props":1489,"children":1491},{"className":1490},[],[1492],{"type":510,"value":1493},"label",{"type":510,"value":1495},"\u002F",{"type":504,"tag":621,"props":1497,"children":1499},{"className":1498},[],[1500],{"type":510,"value":1501},"htmlFor",{"type":510,"value":1503}," 与输入关联（或 aria-label）",{"type":504,"tag":546,"props":1505,"children":1506},{},[1507,1509],{"type":510,"value":1508},"错误呈现：\n",{"type":504,"tag":542,"props":1510,"children":1511},{},[1512,1521],{"type":504,"tag":546,"props":1513,"children":1514},{},[1515],{"type":504,"tag":621,"props":1516,"children":1518},{"className":1517},[],[1519],{"type":510,"value":1520},"aria-invalid",{"type":504,"tag":546,"props":1522,"children":1523},{},[1524,1530],{"type":504,"tag":621,"props":1525,"children":1527},{"className":1526},[],[1528],{"type":510,"value":1529},"aria-describedby",{"type":510,"value":1531}," 指向错误文本",{"type":504,"tag":546,"props":1533,"children":1534},{},[1535,1537],{"type":510,"value":1536},"提交失败：\n",{"type":504,"tag":542,"props":1538,"children":1539},{},[1540,1545],{"type":504,"tag":546,"props":1541,"children":1542},{},[1543],{"type":510,"value":1544},"提供 error summary",{"type":504,"tag":546,"props":1546,"children":1547},{},[1548],{"type":510,"value":1549},"焦点移动到 summary 或第一个错误字段",{"type":504,"tag":512,"props":1551,"children":1552},{},[1553],{"type":504,"tag":516,"props":1554,"children":1555},{},[1556],{"type":510,"value":1557},"经验：可访问性做得好，用户体验和可测试性也会更好。",{"type":504,"tag":530,"props":1559,"children":1561},{"id":1560},"_9-checklist",[1562],{"type":510,"value":1563},"9. Checklist",{"type":504,"tag":542,"props":1565,"children":1568},{"className":1566},[1567],"contains-task-list",[1569,1589,1598,1621,1630,1639],{"type":504,"tag":546,"props":1570,"children":1573},{"className":1571},[1572],"task-list-item",[1574,1580,1582,1587],{"type":504,"tag":1575,"props":1576,"children":1579},"input",{"disabled":1577,"type":1578},true,"checkbox",[],{"type":510,"value":1581}," 是否避免“全表单受控化”？只在必要处用 ",{"type":504,"tag":621,"props":1583,"children":1585},{"className":1584},[],[1586],{"type":510,"value":790},{"type":510,"value":1588},"？",{"type":504,"tag":546,"props":1590,"children":1592},{"className":1591},[1572],[1593,1596],{"type":504,"tag":1575,"props":1594,"children":1595},{"disabled":1577,"type":1578},[],{"type":510,"value":1597}," 校验是否分层（输入期轻量、提交期 schema、后端最终权威）？",{"type":504,"tag":546,"props":1599,"children":1601},{"className":1600},[1572],[1602,1605,1607,1613,1615,1620],{"type":504,"tag":1575,"props":1603,"children":1604},{"disabled":1577,"type":1578},[],{"type":510,"value":1606}," 是否避免全量 ",{"type":504,"tag":621,"props":1608,"children":1610},{"className":1609},[],[1611],{"type":510,"value":1612},"watch()",{"type":510,"value":1614}," \u002F 全量订阅 ",{"type":504,"tag":621,"props":1616,"children":1618},{"className":1617},[],[1619],{"type":510,"value":744},{"type":510,"value":1588},{"type":504,"tag":546,"props":1622,"children":1624},{"className":1623},[1572],[1625,1628],{"type":504,"tag":1575,"props":1626,"children":1627},{"disabled":1577,"type":1578},[],{"type":510,"value":1629}," 提交成功后是否有明确的 invalidate\u002Frefetch 策略（粒度合适）？",{"type":504,"tag":546,"props":1631,"children":1633},{"className":1632},[1572],[1634,1637],{"type":504,"tag":1575,"props":1635,"children":1636},{"disabled":1577,"type":1578},[],{"type":510,"value":1638}," 筛选条件是否以 URL 为真相？写 URL 的时机是否合理（提交\u002F防抖）？",{"type":504,"tag":546,"props":1640,"children":1642},{"className":1641},[1572],[1643,1646],{"type":504,"tag":1575,"props":1644,"children":1645},{"disabled":1577,"type":1578},[],{"type":510,"value":1647}," 错误信息是否能被读屏读到？提交失败后焦点是否可预期？",{"type":504,"tag":530,"props":1649,"children":1651},{"id":1650},"关联阅读",[1652],{"type":510,"value":1650},{"type":504,"tag":542,"props":1654,"children":1655},{},[1656,1671,1685,1699,1713],{"type":504,"tag":546,"props":1657,"children":1658},{},[1659,1661],{"type":510,"value":1660},"状态分层（表单在大系统里的定位）：",{"type":504,"tag":1662,"props":1663,"children":1665},"a",{"href":1664},".\u002F01-state-layering-ui-server-url-global.md",[1666],{"type":504,"tag":621,"props":1667,"children":1669},{"className":1668},[],[1670],{"type":510,"value":1664},{"type":504,"tag":546,"props":1672,"children":1673},{},[1674,1676],{"type":510,"value":1675},"Query 缓存与失效（提交后如何刷新）：",{"type":504,"tag":1662,"props":1677,"children":1679},{"href":1678},".\u002F02-tanstack-query-caching-and-invalidation.md",[1680],{"type":504,"tag":621,"props":1681,"children":1683},{"className":1682},[],[1684],{"type":510,"value":1678},{"type":504,"tag":546,"props":1686,"children":1687},{},[1688,1690],{"type":510,"value":1689},"乐观更新与一致性（下一篇\u002F配套）：",{"type":504,"tag":1662,"props":1691,"children":1693},{"href":1692},".\u002F03-optimistic-updates-and-consistency.md",[1694],{"type":504,"tag":621,"props":1695,"children":1697},{"className":1696},[],[1698],{"type":510,"value":1692},{"type":504,"tag":546,"props":1700,"children":1701},{},[1702,1704],{"type":510,"value":1703},"URL State 心智模型（筛选表单进 URL）：",{"type":504,"tag":1662,"props":1705,"children":1707},{"href":1706},"..\u002F10-routing-and-navigation\u002F01-routing-and-url-state-mental-model.md",[1708],{"type":504,"tag":621,"props":1709,"children":1711},{"className":1710},[],[1712],{"type":510,"value":1706},{"type":504,"tag":546,"props":1714,"children":1715},{},[1716,1718],{"type":510,"value":1717},"Transition（输入流畅 vs 结果区更新）：",{"type":504,"tag":1662,"props":1719,"children":1721},{"href":1720},"..\u002F09-react-18-in-practice\u002F03-transition-useTransition-useDeferredValue.md",[1722],{"type":504,"tag":621,"props":1723,"children":1725},{"className":1724},[],[1726],{"type":510,"value":1720},{"title":1728,"searchDepth":977,"depth":977,"links":1729},"",[1730,1731,1738,1739,1743,1747,1751,1756,1760,1761,1762],{"id":532,"depth":977,"text":535},{"id":596,"depth":977,"text":599,"children":1732},[1733,1734,1735,1736],{"id":603,"depth":1002,"text":606},{"id":670,"depth":1002,"text":673},{"id":713,"depth":1002,"text":716},{"id":781,"depth":1002,"text":1737},"1.4 何时用 Controller",{"id":828,"depth":977,"text":831},{"id":933,"depth":977,"text":936,"children":1740},[1741,1742],{"id":939,"depth":1002,"text":942},{"id":1041,"depth":1002,"text":1044},{"id":1069,"depth":977,"text":1072,"children":1744},[1745,1746],{"id":1075,"depth":1002,"text":1078},{"id":1117,"depth":1002,"text":1120},{"id":1250,"depth":977,"text":1253,"children":1748},[1749,1750],{"id":1256,"depth":1002,"text":1259},{"id":1280,"depth":1002,"text":1283},{"id":1304,"depth":977,"text":1307,"children":1752},[1753,1754,1755],{"id":1310,"depth":1002,"text":1313},{"id":1329,"depth":1002,"text":1332},{"id":1375,"depth":1002,"text":1378},{"id":1411,"depth":977,"text":1414,"children":1757},[1758,1759],{"id":1422,"depth":1002,"text":1425},{"id":1451,"depth":1002,"text":1454},{"id":1478,"depth":977,"text":1481},{"id":1560,"depth":977,"text":1563},{"id":1650,"depth":977,"text":1650},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1764},"\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":1766},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1768},"\u003Cpath fill=\"currentColor\" d=\"m13.172 12l-4.95-4.95l1.414-1.413L16 12l-6.364 6.364l-1.414-1.415z\"\u002F>"]