[{"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-09-react-18-in-practice\u002F04-suspense-loading-boundary-design":494,"i-ri:arrow-left-s-line":1594,"i-ri:menu-2-line":1596,"i-ri:arrow-right-s-line":1598,"i-ep:copy-document":1600},{"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":355,"path":355,"title":356,"updatedAt":21,"meta":498,"body":500,"toc":1569},"bm90ZTpub3Rlcy9yZWFjdC8wOS1yZWFjdC0xOC1pbi1wcmFjdGljZS8wNC1zdXNwZW5zZS1sb2FkaW5nLWJvdW5kYXJ5LWRlc2lnbi5tZA","note","notes\u002Freact\u002F09-react-18-in-practice\u002F04-suspense-loading-boundary-design.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":356,"updatedAt":21,"lastUpdated":21},"react\u002F09-react-18-in-practice\u002F04-suspense-loading-boundary-design",{"type":501,"children":502},"root",[503,511,543,550,555,585,590,631,637,644,649,675,680,686,698,736,742,747,752,758,764,927,932,945,951,964,969,982,990,996,1001,1007,1019,1027,1038,1046,1057,1065,1071,1099,1105,1110,1128,1133,1146,1154,1160,1166,1184,1194,1199,1225,1231,1236,1257,1262,1285,1291,1314,1319,1343,1348,1354,1365,1378,1389,1402,1413,1426,1432,1494,1499,1563],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"suspense加载态边界如何设计机制架构视角",[509],{"type":510,"value":356},"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},"一句话结论：Suspense 的价值不只是“显示一个 loading”，而是把“等待（代码\u002F数据）”变成 React 渲染流程的一等状态——当子树在 render 阶段 ",{"type":504,"tag":522,"props":523,"children":524},"strong",{},[525],{"type":510,"value":526},"suspend",{"type":510,"value":528},"（挂起）时，React 可以在",{"type":504,"tag":522,"props":530,"children":531},{},[532],{"type":510,"value":533},"最近的边界",{"type":510,"value":535},"处选择 fallback、保持旧 UI、或在 SSR\u002FStreaming 下分片吐出内容；因此 Suspense 的关键在于：",{"type":504,"tag":522,"props":537,"children":538},{},[539],{"type":510,"value":540},"边界怎么切、fallback 怎么分层、与路由\u002F缓存\u002F错误处理怎么协作",{"type":510,"value":542},"。",{"type":504,"tag":544,"props":545,"children":547},"h2",{"id":546},"_1-suspense-解决的核心问题",[548],{"type":510,"value":549},"1. Suspense 解决的核心问题",{"type":504,"tag":516,"props":551,"children":552},{},[553],{"type":510,"value":554},"在没有 Suspense 的时代，加载态通常由业务自己拼装：",{"type":504,"tag":556,"props":557,"children":558},"ul",{},[559,572],{"type":504,"tag":560,"props":561,"children":562},"li",{},[563,565],{"type":510,"value":564},"组件内部 ",{"type":504,"tag":566,"props":567,"children":569},"code",{"className":568},[],[570],{"type":510,"value":571},"if (loading) return \u003CSpinner\u002F>",{"type":504,"tag":560,"props":573,"children":574},{},[575,577,583],{"type":510,"value":576},"多个子请求多处 ",{"type":504,"tag":566,"props":578,"children":580},{"className":579},[],[581],{"type":510,"value":582},"loading",{"type":510,"value":584},"，页面会出现“闪烁\u002F抖动\u002F瀑布”",{"type":504,"tag":516,"props":586,"children":587},{},[588],{"type":510,"value":589},"Suspense 把“等待”抽象成渲染层语义，允许你：",{"type":504,"tag":556,"props":591,"children":592},{},[593,603,611,621],{"type":504,"tag":560,"props":594,"children":595},{},[596,601],{"type":504,"tag":522,"props":597,"children":598},{},[599],{"type":510,"value":600},"在一个边界内统一管理加载态",{"type":510,"value":602},"（避免到处散落 loading 判断）",{"type":504,"tag":560,"props":604,"children":605},{},[606],{"type":504,"tag":522,"props":607,"children":608},{},[609],{"type":510,"value":610},"把加载态做成“骨架\u002F占位”而不是整页空白",{"type":504,"tag":560,"props":612,"children":613},{},[614,619],{"type":504,"tag":522,"props":615,"children":616},{},[617],{"type":510,"value":618},"在并发渲染中保持交互响应",{"type":510,"value":620},"（配合 Transition）",{"type":504,"tag":560,"props":622,"children":623},{},[624,629],{"type":504,"tag":522,"props":625,"children":626},{},[627],{"type":510,"value":628},"在 SSR 下进行 Streaming",{"type":510,"value":630},"：先把壳（shell）返回，再逐块补齐内容",{"type":504,"tag":544,"props":632,"children":634},{"id":633},"_2-机制什么叫suspendreact-做了什么",[635],{"type":510,"value":636},"2. 机制：什么叫“suspend”？React 做了什么？",{"type":504,"tag":638,"props":639,"children":641},"h3",{"id":640},"_21-render-阶段子树挂起suspend",[642],{"type":510,"value":643},"2.1 Render 阶段：子树挂起（suspend）",{"type":504,"tag":516,"props":645,"children":646},{},[647],{"type":510,"value":648},"Suspense 的关键机制是：",{"type":504,"tag":556,"props":650,"children":651},{},[652,657,662],{"type":504,"tag":560,"props":653,"children":654},{},[655],{"type":510,"value":656},"某个组件在 render 过程中遇到“当前无法继续渲染”的情况",{"type":504,"tag":560,"props":658,"children":659},{},[660],{"type":510,"value":661},"它会以约定的方式把“我需要等待”的信号交给 React（常见形式是抛出一个 promise\u002Fthenable）",{"type":504,"tag":560,"props":663,"children":664},{},[665,667,673],{"type":510,"value":666},"React 从当前位置向上寻找最近的 ",{"type":504,"tag":566,"props":668,"children":670},{"className":669},[],[671],{"type":510,"value":672},"\u003CSuspense fallback=...>",{"type":510,"value":674}," 边界",{"type":504,"tag":516,"props":676,"children":677},{},[678],{"type":510,"value":679},"找到边界后，React 会把这次渲染标记为“该边界挂起”。",{"type":504,"tag":638,"props":681,"children":683},{"id":682},"_22-commit-阶段显示-fallback-或保留旧-ui",[684],{"type":510,"value":685},"2.2 Commit 阶段：显示 fallback 或保留旧 UI",{"type":504,"tag":516,"props":687,"children":688},{},[689,691,696],{"type":510,"value":690},"由于 ",{"type":504,"tag":522,"props":692,"children":693},{},[694],{"type":510,"value":695},"render 可中断、commit 不可中断",{"type":510,"value":697},"：",{"type":504,"tag":556,"props":699,"children":700},{},[701,706],{"type":504,"tag":560,"props":702,"children":703},{},[704],{"type":510,"value":705},"React 不会把“半渲染的中间态”提交到 DOM",{"type":504,"tag":560,"props":707,"children":708},{},[709,711],{"type":510,"value":710},"React 会在 commit 时选择：\n",{"type":504,"tag":556,"props":712,"children":713},{},[714,724],{"type":504,"tag":560,"props":715,"children":716},{},[717,722],{"type":504,"tag":522,"props":718,"children":719},{},[720],{"type":510,"value":721},"提交 fallback",{"type":510,"value":723},"（显示 loading UI）",{"type":504,"tag":560,"props":725,"children":726},{},[727,729,734],{"type":510,"value":728},"或者在某些情况下（比如与 Transition 配合）",{"type":504,"tag":522,"props":730,"children":731},{},[732],{"type":510,"value":733},"保留旧 UI",{"type":510,"value":735},"，让新 UI 在后台准备好",{"type":504,"tag":638,"props":737,"children":739},{"id":738},"_23-promise-resolveping-重新调度",[740],{"type":510,"value":741},"2.3 Promise resolve：ping → 重新调度",{"type":504,"tag":516,"props":743,"children":744},{},[745],{"type":510,"value":746},"当挂起的资源就绪（promise resolve），React 会触发一次“ping”，把对应的工作重新放回调度队列。",{"type":504,"tag":516,"props":748,"children":749},{},[750],{"type":510,"value":751},"这和你笔记里 lane 模型的概念可以对上：挂起的 lanes 会进入 suspended\u002Fpinged 等状态，然后被重新选择。",{"type":504,"tag":544,"props":753,"children":755},{"id":754},"_3-suspense-的两类典型使用方式",[756],{"type":510,"value":757},"3. Suspense 的两类典型使用方式",{"type":504,"tag":638,"props":759,"children":761},{"id":760},"_31-代码分割最稳最通用",[762],{"type":510,"value":763},"3.1 代码分割（最稳、最通用）",{"type":504,"tag":765,"props":766,"children":771},"pre",{"className":767,"code":768,"language":769,"meta":770,"style":770},"language-tsx shiki shiki-themes github-light github-dark","const SettingsPage = React.lazy(() => import('.\u002FSettingsPage'))\n\n\u003CSuspense fallback={\u003CPageSkeleton \u002F>}>\n  \u003CSettingsPage \u002F>\n\u003C\u002FSuspense>\n","tsx","",[772],{"type":504,"tag":566,"props":773,"children":774},{"__ignoreMap":770},[775,841,851,890,909],{"type":504,"tag":776,"props":777,"children":780},"span",{"class":778,"line":779},"line",1,[781,787,793,798,804,810,815,820,825,830,836],{"type":504,"tag":776,"props":782,"children":784},{"style":783},"--shiki-default:#D73A49;--shiki-dark:#F97583",[785],{"type":510,"value":786},"const",{"type":504,"tag":776,"props":788,"children":790},{"style":789},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[791],{"type":510,"value":792}," SettingsPage",{"type":504,"tag":776,"props":794,"children":795},{"style":783},[796],{"type":510,"value":797}," =",{"type":504,"tag":776,"props":799,"children":801},{"style":800},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[802],{"type":510,"value":803}," React.",{"type":504,"tag":776,"props":805,"children":807},{"style":806},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[808],{"type":510,"value":809},"lazy",{"type":504,"tag":776,"props":811,"children":812},{"style":800},[813],{"type":510,"value":814},"(() ",{"type":504,"tag":776,"props":816,"children":817},{"style":783},[818],{"type":510,"value":819},"=>",{"type":504,"tag":776,"props":821,"children":822},{"style":783},[823],{"type":510,"value":824}," import",{"type":504,"tag":776,"props":826,"children":827},{"style":800},[828],{"type":510,"value":829},"(",{"type":504,"tag":776,"props":831,"children":833},{"style":832},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[834],{"type":510,"value":835},"'.\u002FSettingsPage'",{"type":504,"tag":776,"props":837,"children":838},{"style":800},[839],{"type":510,"value":840},"))\n",{"type":504,"tag":776,"props":842,"children":844},{"class":778,"line":843},2,[845],{"type":504,"tag":776,"props":846,"children":848},{"emptyLinePlaceholder":847},true,[849],{"type":510,"value":850},"\n",{"type":504,"tag":776,"props":852,"children":854},{"class":778,"line":853},3,[855,860,865,870,875,880,885],{"type":504,"tag":776,"props":856,"children":857},{"style":800},[858],{"type":510,"value":859},"\u003C",{"type":504,"tag":776,"props":861,"children":862},{"style":789},[863],{"type":510,"value":864},"Suspense",{"type":504,"tag":776,"props":866,"children":867},{"style":806},[868],{"type":510,"value":869}," fallback",{"type":504,"tag":776,"props":871,"children":872},{"style":783},[873],{"type":510,"value":874},"=",{"type":504,"tag":776,"props":876,"children":877},{"style":800},[878],{"type":510,"value":879},"{\u003C",{"type":504,"tag":776,"props":881,"children":882},{"style":789},[883],{"type":510,"value":884},"PageSkeleton",{"type":504,"tag":776,"props":886,"children":887},{"style":800},[888],{"type":510,"value":889}," \u002F>}>\n",{"type":504,"tag":776,"props":891,"children":893},{"class":778,"line":892},4,[894,899,904],{"type":504,"tag":776,"props":895,"children":896},{"style":800},[897],{"type":510,"value":898},"  \u003C",{"type":504,"tag":776,"props":900,"children":901},{"style":789},[902],{"type":510,"value":903},"SettingsPage",{"type":504,"tag":776,"props":905,"children":906},{"style":800},[907],{"type":510,"value":908}," \u002F>\n",{"type":504,"tag":776,"props":910,"children":912},{"class":778,"line":911},5,[913,918,922],{"type":504,"tag":776,"props":914,"children":915},{"style":800},[916],{"type":510,"value":917},"\u003C\u002F",{"type":504,"tag":776,"props":919,"children":920},{"style":789},[921],{"type":510,"value":864},{"type":504,"tag":776,"props":923,"children":924},{"style":800},[925],{"type":510,"value":926},">\n",{"type":504,"tag":516,"props":928,"children":929},{},[930],{"type":510,"value":931},"特点：",{"type":504,"tag":556,"props":933,"children":934},{},[935,940],{"type":504,"tag":560,"props":936,"children":937},{},[938],{"type":510,"value":939},"suspend 的来源是“JS chunk 还没加载完”",{"type":504,"tag":560,"props":941,"children":942},{},[943],{"type":510,"value":944},"对于 Vite \u002F Webpack 等环境都成立",{"type":504,"tag":638,"props":946,"children":948},{"id":947},"_32-数据加载需要-suspense-aware-的数据源框架",[949],{"type":510,"value":950},"3.2 数据加载（需要 Suspense-aware 的数据源\u002F框架）",{"type":504,"tag":516,"props":952,"children":953},{},[954,956,962],{"type":510,"value":955},"在“纯 React”里，Suspense 并不会自动把 ",{"type":504,"tag":566,"props":957,"children":959},{"className":958},[],[960],{"type":510,"value":961},"fetch()",{"type":510,"value":963}," 变成 suspend。",{"type":504,"tag":516,"props":965,"children":966},{},[967],{"type":510,"value":968},"要让数据请求与 Suspense 协作，需要：",{"type":504,"tag":556,"props":970,"children":971},{},[972,977],{"type":504,"tag":560,"props":973,"children":974},{},[975],{"type":510,"value":976},"框架（例如 Next App Router 的数据获取与渲染模型）",{"type":504,"tag":560,"props":978,"children":979},{},[980],{"type":510,"value":981},"或数据层库提供 suspense 能力（例如 TanStack Query 提供 suspense 版本 hooks\u002F配置）",{"type":504,"tag":512,"props":983,"children":984},{},[985],{"type":504,"tag":516,"props":986,"children":987},{},[988],{"type":510,"value":989},"架构建议：不要在不了解边界的情况下，手写“throw promise”式的 data wrapper 扩散到业务里；最好收敛在数据层。",{"type":504,"tag":544,"props":991,"children":993},{"id":992},"_4-边界怎么切suspense-设计的核心",[994],{"type":510,"value":995},"4. “边界怎么切”：Suspense 设计的核心",{"type":504,"tag":516,"props":997,"children":998},{},[999],{"type":510,"value":1000},"你可以把 Suspense 当成“加载态的路由\u002F组件分区”。边界切得好，页面体验会像原生应用；切不好就会出现：整页白屏、局部抖动、闪烁、瀑布请求。",{"type":504,"tag":638,"props":1002,"children":1004},{"id":1003},"_41-三个原则",[1005],{"type":510,"value":1006},"4.1 三个原则",{"type":504,"tag":1008,"props":1009,"children":1010},"ol",{},[1011],{"type":504,"tag":560,"props":1012,"children":1013},{},[1014],{"type":504,"tag":522,"props":1015,"children":1016},{},[1017],{"type":510,"value":1018},"壳（Shell）要稳定",{"type":504,"tag":556,"props":1020,"children":1021},{},[1022],{"type":504,"tag":560,"props":1023,"children":1024},{},[1025],{"type":510,"value":1026},"Header\u002F侧边栏\u002F导航\u002F输入框等“交互壳”尽量不要被大边界包住",{"type":504,"tag":1008,"props":1028,"children":1029},{"start":843},[1030],{"type":504,"tag":560,"props":1031,"children":1032},{},[1033],{"type":504,"tag":522,"props":1034,"children":1035},{},[1036],{"type":510,"value":1037},"边界要贴合 UX 单元",{"type":504,"tag":556,"props":1039,"children":1040},{},[1041],{"type":504,"tag":560,"props":1042,"children":1043},{},[1044],{"type":510,"value":1045},"用户能理解的区域：列表区、详情区、评论区、图表区",{"type":504,"tag":1008,"props":1047,"children":1048},{"start":853},[1049],{"type":504,"tag":560,"props":1050,"children":1051},{},[1052],{"type":504,"tag":522,"props":1053,"children":1054},{},[1055],{"type":510,"value":1056},"边界要分层",{"type":504,"tag":556,"props":1058,"children":1059},{},[1060],{"type":504,"tag":560,"props":1061,"children":1062},{},[1063],{"type":510,"value":1064},"route-level（页面骨架） + section-level（模块骨架） + widget-level（小占位）",{"type":504,"tag":638,"props":1066,"children":1068},{"id":1067},"_42-常见反例",[1069],{"type":510,"value":1070},"4.2 常见反例",{"type":504,"tag":556,"props":1072,"children":1073},{},[1074,1087],{"type":504,"tag":560,"props":1075,"children":1076},{},[1077,1079,1085],{"type":510,"value":1078},"单个巨大 ",{"type":504,"tag":566,"props":1080,"children":1082},{"className":1081},[],[1083],{"type":510,"value":1084},"\u003CSuspense>",{"type":510,"value":1086}," 包住整个 App → 任意一块数据慢都会导致整页 fallback（等于白屏）",{"type":504,"tag":560,"props":1088,"children":1089},{},[1090,1092,1097],{"type":510,"value":1091},"很碎的 ",{"type":504,"tag":566,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":510,"value":1084},{"type":510,"value":1098}," 到处都是 → fallback 四处闪，视觉噪音很强",{"type":504,"tag":544,"props":1100,"children":1102},{"id":1101},"_5-与-transition-的协作避免页面闪退到-fallback",[1103],{"type":510,"value":1104},"5. 与 Transition 的协作：避免“页面闪退到 fallback”",{"type":504,"tag":516,"props":1106,"children":1107},{},[1108],{"type":510,"value":1109},"在路由切页或筛选时，如果你把更新标记为 Transition（非紧急），React 更倾向于：",{"type":504,"tag":556,"props":1111,"children":1112},{},[1113,1118,1123],{"type":504,"tag":560,"props":1114,"children":1115},{},[1116],{"type":510,"value":1117},"保持旧 UI",{"type":504,"tag":560,"props":1119,"children":1120},{},[1121],{"type":510,"value":1122},"让新 UI 在后台准备",{"type":504,"tag":560,"props":1124,"children":1125},{},[1126],{"type":510,"value":1127},"准备好后一次性切换",{"type":504,"tag":516,"props":1129,"children":1130},{},[1131],{"type":510,"value":1132},"这能显著减少：",{"type":504,"tag":556,"props":1134,"children":1135},{},[1136,1141],{"type":504,"tag":560,"props":1137,"children":1138},{},[1139],{"type":510,"value":1140},"频繁进入 fallback 导致的闪烁",{"type":504,"tag":560,"props":1142,"children":1143},{},[1144],{"type":510,"value":1145},"输入卡顿",{"type":504,"tag":512,"props":1147,"children":1148},{},[1149],{"type":504,"tag":516,"props":1150,"children":1151},{},[1152],{"type":510,"value":1153},"经验：切页\u002F筛选的“结果区更新”通常是 Transition 候选；而输入框字符、点击反馈是 urgent。",{"type":504,"tag":544,"props":1155,"children":1157},{"id":1156},"_6-在两套技术栈里怎么落地",[1158],{"type":510,"value":1159},"6. 在两套技术栈里怎么落地",{"type":504,"tag":638,"props":1161,"children":1163},{"id":1162},"_61-tanstack-router-vite客户端应用",[1164],{"type":510,"value":1165},"6.1 TanStack Router + Vite（客户端应用）",{"type":504,"tag":516,"props":1167,"children":1168},{},[1169,1174,1176,1182],{"type":504,"tag":522,"props":1170,"children":1171},{},[1172],{"type":510,"value":1173},"代码分割",{"type":510,"value":1175},"：路由组件用 ",{"type":504,"tag":566,"props":1177,"children":1179},{"className":1178},[],[1180],{"type":510,"value":1181},"React.lazy + Suspense",{"type":510,"value":1183}," 做 route-level fallback。",{"type":504,"tag":516,"props":1185,"children":1186},{},[1187,1192],{"type":504,"tag":522,"props":1188,"children":1189},{},[1190],{"type":510,"value":1191},"数据加载",{"type":510,"value":1193},"：推荐用 TanStack Query 的 suspense 能力（例如 suspense hooks 或 suspense 配置），把“数据等待”转为 suspend，然后在 section-level 边界展示骨架。",{"type":504,"tag":516,"props":1195,"children":1196},{},[1197],{"type":510,"value":1198},"建议形态：",{"type":504,"tag":556,"props":1200,"children":1201},{},[1202,1207,1212],{"type":504,"tag":560,"props":1203,"children":1204},{},[1205],{"type":510,"value":1206},"route-level：页面骨架（layout 保持稳定）",{"type":504,"tag":560,"props":1208,"children":1209},{},[1210],{"type":510,"value":1211},"section-level：列表\u002F详情等模块各自边界",{"type":504,"tag":560,"props":1213,"children":1214},{},[1215,1217,1223],{"type":510,"value":1216},"配合 ",{"type":504,"tag":566,"props":1218,"children":1220},{"className":1219},[],[1221],{"type":510,"value":1222},"useTransition",{"type":510,"value":1224},"：导航\u002F筛选时显示轻量 pending（顶部进度条\u002F按钮 pending），尽量不把用户踢回整块 fallback",{"type":504,"tag":638,"props":1226,"children":1228},{"id":1227},"_62-nextjs-app-router全栈ssrstreaming",[1229],{"type":510,"value":1230},"6.2 Next.js App Router（全栈\u002FSSR\u002FStreaming）",{"type":504,"tag":516,"props":1232,"children":1233},{},[1234],{"type":510,"value":1235},"在 Next App Router 下，Suspense 与 streaming 是“天然一体”的：",{"type":504,"tag":556,"props":1237,"children":1238},{},[1239,1252],{"type":504,"tag":560,"props":1240,"children":1241},{},[1242,1244,1250],{"type":510,"value":1243},"segment 可以通过 ",{"type":504,"tag":566,"props":1245,"children":1247},{"className":1246},[],[1248],{"type":510,"value":1249},"loading.tsx",{"type":510,"value":1251}," 定义该段的 fallback",{"type":504,"tag":560,"props":1253,"children":1254},{},[1255],{"type":510,"value":1256},"服务端可以先返回页面壳，再逐块把内容流式推送",{"type":504,"tag":516,"props":1258,"children":1259},{},[1260],{"type":510,"value":1261},"设计要点：",{"type":504,"tag":556,"props":1263,"children":1264},{},[1265,1275,1280],{"type":504,"tag":560,"props":1266,"children":1267},{},[1268,1273],{"type":504,"tag":522,"props":1269,"children":1270},{},[1271],{"type":510,"value":1272},"把 loading 放在“段（segment）层级”而不是全局",{"type":510,"value":1274},"：避免整页白屏",{"type":504,"tag":560,"props":1276,"children":1277},{},[1278],{"type":510,"value":1279},"与缓存\u002F重验证策略协作：减少不必要的挂起",{"type":504,"tag":560,"props":1281,"children":1282},{},[1283],{"type":510,"value":1284},"与错误边界协作：loading 解决“等”，error boundary 解决“错”",{"type":504,"tag":544,"props":1286,"children":1288},{"id":1287},"_7-与-error-boundary-的关系别把错和等混为一谈",[1289],{"type":510,"value":1290},"7. 与 Error Boundary 的关系：别把“错”和“等”混为一谈",{"type":504,"tag":556,"props":1292,"children":1293},{},[1294,1304],{"type":504,"tag":560,"props":1295,"children":1296},{},[1297,1299],{"type":510,"value":1298},"Suspense 处理的是 ",{"type":504,"tag":522,"props":1300,"children":1301},{},[1302],{"type":510,"value":1303},"等待（pending）",{"type":504,"tag":560,"props":1305,"children":1306},{},[1307,1309],{"type":510,"value":1308},"Error Boundary 处理的是 ",{"type":504,"tag":522,"props":1310,"children":1311},{},[1312],{"type":510,"value":1313},"异常（error）",{"type":504,"tag":516,"props":1315,"children":1316},{},[1317],{"type":510,"value":1318},"真实项目里你经常需要：",{"type":504,"tag":556,"props":1320,"children":1321},{},[1322,1332],{"type":504,"tag":560,"props":1323,"children":1324},{},[1325,1330],{"type":504,"tag":566,"props":1326,"children":1328},{"className":1327},[],[1329],{"type":510,"value":864},{"type":510,"value":1331}," 包住“可能等”的区域",{"type":504,"tag":560,"props":1333,"children":1334},{},[1335,1341],{"type":504,"tag":566,"props":1336,"children":1338},{"className":1337},[],[1339],{"type":510,"value":1340},"ErrorBoundary",{"type":510,"value":1342}," 包住“可能错”的区域",{"type":504,"tag":516,"props":1344,"children":1345},{},[1346],{"type":510,"value":1347},"并且两者都应该分层（route\u002Fsection\u002Fwidget），避免一个错误炸掉整页。",{"type":504,"tag":544,"props":1349,"children":1351},{"id":1350},"_8-常见坑与排查思路",[1352],{"type":510,"value":1353},"8. 常见坑与排查思路",{"type":504,"tag":1008,"props":1355,"children":1356},{},[1357],{"type":504,"tag":560,"props":1358,"children":1359},{},[1360],{"type":504,"tag":522,"props":1361,"children":1362},{},[1363],{"type":510,"value":1364},"瀑布（waterfall）加载",{"type":504,"tag":556,"props":1366,"children":1367},{},[1368,1373],{"type":504,"tag":560,"props":1369,"children":1370},{},[1371],{"type":510,"value":1372},"多个模块串行等待导致总耗时变长",{"type":504,"tag":560,"props":1374,"children":1375},{},[1376],{"type":510,"value":1377},"解决：并行预取、提升到更上层一次性请求、或拆分 streaming（Next）",{"type":504,"tag":1008,"props":1379,"children":1380},{"start":843},[1381],{"type":504,"tag":560,"props":1382,"children":1383},{},[1384],{"type":504,"tag":522,"props":1385,"children":1386},{},[1387],{"type":510,"value":1388},"fallback 抖动（thrash）",{"type":504,"tag":556,"props":1390,"children":1391},{},[1392,1397],{"type":504,"tag":560,"props":1393,"children":1394},{},[1395],{"type":510,"value":1396},"边界切得太碎 + 更新频繁",{"type":504,"tag":560,"props":1398,"children":1399},{},[1400],{"type":510,"value":1401},"解决：提升边界层级\u002F用 Transition 保持旧 UI\u002F对 fallback 做延迟展示（避免 50ms 内闪一下）",{"type":504,"tag":1008,"props":1403,"children":1404},{"start":853},[1405],{"type":504,"tag":560,"props":1406,"children":1407},{},[1408],{"type":504,"tag":522,"props":1409,"children":1410},{},[1411],{"type":510,"value":1412},"Hydration mismatch（SSR 水合不一致）",{"type":504,"tag":556,"props":1414,"children":1415},{},[1416,1421],{"type":504,"tag":560,"props":1417,"children":1418},{},[1419],{"type":510,"value":1420},"fallback\u002F条件渲染在 server 与 client 不一致",{"type":504,"tag":560,"props":1422,"children":1423},{},[1424],{"type":510,"value":1425},"解决：保证初始渲染一致；避免依赖仅客户端可得的值影响首屏结构",{"type":504,"tag":544,"props":1427,"children":1429},{"id":1428},"_9-checklist写完一页-suspense-结构后自检",[1430],{"type":510,"value":1431},"9. Checklist（写完一页 Suspense 结构后自检）",{"type":504,"tag":556,"props":1433,"children":1436},{"className":1434},[1435],"contains-task-list",[1437,1449,1458,1467,1476,1485],{"type":504,"tag":560,"props":1438,"children":1441},{"className":1439},[1440],"task-list-item",[1442,1447],{"type":504,"tag":1443,"props":1444,"children":1446},"input",{"disabled":847,"type":1445},"checkbox",[],{"type":510,"value":1448}," 壳（Header\u002FNav\u002F输入）是否稳定，不会因任意数据慢而变成 fallback？",{"type":504,"tag":560,"props":1450,"children":1452},{"className":1451},[1440],[1453,1456],{"type":504,"tag":1443,"props":1454,"children":1455},{"disabled":847,"type":1445},[],{"type":510,"value":1457}," 是否存在一个过大的边界导致整页白屏？",{"type":504,"tag":560,"props":1459,"children":1461},{"className":1460},[1440],[1462,1465],{"type":504,"tag":1443,"props":1463,"children":1464},{"disabled":847,"type":1445},[],{"type":510,"value":1466}," 是否边界过碎导致 fallback 闪烁？",{"type":504,"tag":560,"props":1468,"children":1470},{"className":1469},[1440],[1471,1474],{"type":504,"tag":1443,"props":1472,"children":1473},{"disabled":847,"type":1445},[],{"type":510,"value":1475}," 路由切页\u002F筛选是否可以用 Transition 保持旧 UI？",{"type":504,"tag":560,"props":1477,"children":1479},{"className":1478},[1440],[1480,1483],{"type":504,"tag":1443,"props":1481,"children":1482},{"disabled":847,"type":1445},[],{"type":510,"value":1484}," 数据层是否有缓存\u002F预取\u002F去重？Suspense 不是性能银弹",{"type":504,"tag":560,"props":1486,"children":1488},{"className":1487},[1440],[1489,1492],{"type":504,"tag":1443,"props":1490,"children":1491},{"disabled":847,"type":1445},[],{"type":510,"value":1493}," 等待与错误是否分别用 Suspense 与 ErrorBoundary 分治？",{"type":504,"tag":544,"props":1495,"children":1497},{"id":1496},"关联阅读",[1498],{"type":510,"value":1496},{"type":504,"tag":556,"props":1500,"children":1501},{},[1502,1518,1533,1548],{"type":504,"tag":560,"props":1503,"children":1504},{},[1505,1507],{"type":510,"value":1506},"Transition（机制）：",{"type":504,"tag":1508,"props":1509,"children":1511},"a",{"href":1510},".\u002F03-transition-useTransition-useDeferredValue.md",[1512],{"type":504,"tag":566,"props":1513,"children":1515},{"className":1514},[],[1516],{"type":510,"value":1517},"notes\u002F09-react-18-in-practice\u002F03-transition-useTransition-useDeferredValue.md",{"type":504,"tag":560,"props":1519,"children":1520},{},[1521,1523],{"type":510,"value":1522},"Lane 模型：",{"type":504,"tag":1508,"props":1524,"children":1526},{"href":1525},"..\u002F03-concurrent-mode\u002F13-lane-model.md",[1527],{"type":504,"tag":566,"props":1528,"children":1530},{"className":1529},[],[1531],{"type":510,"value":1532},"notes\u002F03-concurrent-mode\u002F13-lane-model.md",{"type":504,"tag":560,"props":1534,"children":1535},{},[1536,1538],{"type":510,"value":1537},"优先级设计：",{"type":504,"tag":1508,"props":1539,"children":1541},{"href":1540},"..\u002F03-concurrent-mode\u002F12-priority-design.md",[1542],{"type":504,"tag":566,"props":1543,"children":1545},{"className":1544},[],[1546],{"type":510,"value":1547},"notes\u002F03-concurrent-mode\u002F12-priority-design.md",{"type":504,"tag":560,"props":1549,"children":1550},{},[1551,1553],{"type":510,"value":1552},"时间切片：",{"type":504,"tag":1508,"props":1554,"children":1556},{"href":1555},"..\u002F03-concurrent-mode\u002F10-time-slicing.md",[1557],{"type":504,"tag":566,"props":1558,"children":1560},{"className":1559},[],[1561],{"type":510,"value":1562},"notes\u002F03-concurrent-mode\u002F10-time-slicing.md",{"type":504,"tag":1564,"props":1565,"children":1566},"style",{},[1567],{"type":510,"value":1568},"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":770,"searchDepth":843,"depth":843,"links":1570},[1571,1572,1577,1581,1585,1586,1590,1591,1592,1593],{"id":546,"depth":843,"text":549},{"id":633,"depth":843,"text":636,"children":1573},[1574,1575,1576],{"id":640,"depth":853,"text":643},{"id":682,"depth":853,"text":685},{"id":738,"depth":853,"text":741},{"id":754,"depth":843,"text":757,"children":1578},[1579,1580],{"id":760,"depth":853,"text":763},{"id":947,"depth":853,"text":950},{"id":992,"depth":843,"text":995,"children":1582},[1583,1584],{"id":1003,"depth":853,"text":1006},{"id":1067,"depth":853,"text":1070},{"id":1101,"depth":843,"text":1104},{"id":1156,"depth":843,"text":1159,"children":1587},[1588,1589],{"id":1162,"depth":853,"text":1165},{"id":1227,"depth":853,"text":1230},{"id":1287,"depth":843,"text":1290},{"id":1350,"depth":843,"text":1353},{"id":1428,"depth":843,"text":1431},{"id":1496,"depth":843,"text":1496},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1595},"\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":1597},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1599},"\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":1601,"height":1601,"rotate":4,"vFlip":6,"hFlip":6,"body":1602},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>"]