[{"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-15-engineering-and-architecture\u002F05-bff-and-api-design":494,"i-ri:arrow-left-s-line":1613,"i-ri:menu-2-line":1615,"i-ri:arrow-right-s-line":1617,"i-ep:copy-document":1619},{"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":467,"path":467,"title":468,"updatedAt":21,"meta":498,"body":500,"toc":1596},"bm90ZTpub3Rlcy9yZWFjdC8xNS1lbmdpbmVlcmluZy1hbmQtYXJjaGl0ZWN0dXJlLzA1LWJmZi1hbmQtYXBpLWRlc2lnbi5tZA","note","notes\u002Freact\u002F15-engineering-and-architecture\u002F05-bff-and-api-design.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":468,"updatedAt":21,"lastUpdated":21},"react\u002F15-engineering-and-architecture\u002F05-bff-and-api-design",{"type":501,"children":502},"root",[503,511,527,531,538,550,563,570,625,631,666,669,675,680,686,691,898,904,909,958,968,991,997,1002,1011,1126,1135,1234,1239,1242,1248,1253,1294,1307,1310,1316,1321,1327,1451,1457,1494,1504,1507,1513,1590],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"bff-与-api-设计前端架构师的视角",[509],{"type":510,"value":468},"text",{"type":504,"tag":512,"props":513,"children":514},"blockquote",{},[515],{"type":504,"tag":516,"props":517,"children":518},"p",{},[519,525],{"type":504,"tag":520,"props":521,"children":522},"strong",{},[523],{"type":510,"value":524},"一句话结论",{"type":510,"value":526},"：采用 BFF (Backend for Frontend) 模式，并遵循 RESTful 或 GraphQL 等成熟的接口规范，设计出具备良好缓存策略、一致性错误处理和明确鉴权模式的 API，是实现前后端高效协作、提升应用整体性能与体验的关键。",{"type":504,"tag":528,"props":529,"children":530},"hr",{},[],{"type":504,"tag":532,"props":533,"children":535},"h2",{"id":534},"_1-bff-backend-for-frontend-模式",[536],{"type":510,"value":537},"1. BFF (Backend for Frontend) 模式",{"type":504,"tag":516,"props":539,"children":540},{},[541,543,548],{"type":510,"value":542},"BFF 是一种架构模式，它在通用的后端服务（Microservices 或单体服务）与前端应用之间增加了一个专门的中间层。这个中间层的核心职责是",{"type":504,"tag":520,"props":544,"children":545},{},[546],{"type":510,"value":547},"服务于特定的前端体验",{"type":510,"value":549},"。",{"type":504,"tag":551,"props":552,"children":556},"pre",{"className":553,"code":555,"language":510},[554],"language-text","+-----------+      +-------------------+      +-----------------+\n| Mobile App|----->| Mobile BFF        |----->|                 |\n+-----------+      +-------------------+      |                 |\n                                              |  Generic Backend  |\n+-----------+      +-------------------+      |   (Services)    |\n| Web App   |----->| Web BFF           |----->|                 |\n+-----------+      +-------------------+      |                 |\n",[557],{"type":504,"tag":558,"props":559,"children":561},"code",{"__ignoreMap":560},"",[562],{"type":510,"value":555},{"type":504,"tag":564,"props":565,"children":567},"h3",{"id":566},"a-bff-的价值",[568],{"type":510,"value":569},"A. BFF 的价值",{"type":504,"tag":571,"props":572,"children":573},"ol",{},[574,585,595,605,615],{"type":504,"tag":575,"props":576,"children":577},"li",{},[578,583],{"type":504,"tag":520,"props":579,"children":580},{},[581],{"type":510,"value":582},"聚合与裁剪数据",{"type":510,"value":584},"：前端页面通常需要来自多个后端微服务的数据。BFF 可以将这些请求聚合起来，一次性返回给前端，减少网络往返。同时，它可以裁剪掉前端不需要的字段，减小 payload 体积。",{"type":504,"tag":575,"props":586,"children":587},{},[588,593],{"type":504,"tag":520,"props":589,"children":590},{},[591],{"type":510,"value":592},"适配前端模型",{"type":510,"value":594},"：后端服务的数据模型可能与前端需要展示的视图模型不匹配。BFF 负责进行数据转换、格式化和组装，为前端提供“即插即用”的数据。",{"type":504,"tag":575,"props":596,"children":597},{},[598,603],{"type":504,"tag":520,"props":599,"children":600},{},[601],{"type":510,"value":602},"减轻前端负担",{"type":510,"value":604},"：将一些复杂的、与 UI 无关的逻辑（如数据计算、业务规则验证）从前端移到 BFF，使前端更专注于 UI 渲染。",{"type":504,"tag":575,"props":606,"children":607},{},[608,613],{"type":504,"tag":520,"props":609,"children":610},{},[611],{"type":510,"value":612},"技术异构与协议转换",{"type":510,"value":614},"：BFF 可以作为不同技术栈之间的桥梁。例如，后端服务使用 gRPC，而 BFF 可以通过 REST 或 GraphQL API 将其暴露给前端。",{"type":504,"tag":575,"props":616,"children":617},{},[618,623],{"type":504,"tag":520,"props":619,"children":620},{},[621],{"type":510,"value":622},"增强安全性",{"type":510,"value":624},"：BFF 可以作为一道安全屏障，将敏感的 API 密钥或复杂的认证逻辑对前端隐藏。",{"type":504,"tag":564,"props":626,"children":628},{"id":627},"b-谁来开发-bff",[629],{"type":510,"value":630},"B. 谁来开发 BFF？",{"type":504,"tag":516,"props":632,"children":633},{},[634,636,641,643,648,650,656,658,664],{"type":510,"value":635},"理想情况下，BFF 由",{"type":504,"tag":520,"props":637,"children":638},{},[639],{"type":510,"value":640},"前端团队",{"type":510,"value":642},"或",{"type":504,"tag":520,"props":644,"children":645},{},[646],{"type":510,"value":647},"全栈团队",{"type":510,"value":649},"拥有和开发。因为 BFF 的需求直接来源于前端，由最理解前端需求的团队来开发，可以最大化其价值，减少跨团队沟通成本。像 Next.js 这样的全栈框架，其 ",{"type":504,"tag":558,"props":651,"children":653},{"className":652},[],[654],{"type":510,"value":655},"app\u002Fapi\u002F",{"type":510,"value":657}," 或 ",{"type":504,"tag":558,"props":659,"children":661},{"className":660},[],[662],{"type":510,"value":663},"pages\u002Fapi\u002F",{"type":510,"value":665}," 目录就是实现 BFF 的绝佳场所。",{"type":504,"tag":528,"props":667,"children":668},{},[],{"type":504,"tag":532,"props":670,"children":672},{"id":671},"_2-api-接口规范",[673],{"type":510,"value":674},"2. API 接口规范",{"type":504,"tag":516,"props":676,"children":677},{},[678],{"type":510,"value":679},"无论是否使用 BFF，一套清晰、一致的接口规范都是高效协作的基础。",{"type":504,"tag":564,"props":681,"children":683},{"id":682},"a-restful-api",[684],{"type":510,"value":685},"A. RESTful API",{"type":504,"tag":516,"props":687,"children":688},{},[689],{"type":510,"value":690},"REST (Representational State Transfer) 是一套广为接受的 API 设计风格。",{"type":504,"tag":692,"props":693,"children":694},"ul",{},[695,720,799],{"type":504,"tag":575,"props":696,"children":697},{},[698,703,705,711,713,719],{"type":504,"tag":520,"props":699,"children":700},{},[701],{"type":510,"value":702},"资源 (Resources)",{"type":510,"value":704},"：通过 URL 路径来识别资源，如 ",{"type":504,"tag":558,"props":706,"children":708},{"className":707},[],[709],{"type":510,"value":710},"\u002Fusers",{"type":510,"value":712},", ",{"type":504,"tag":558,"props":714,"children":716},{"className":715},[],[717],{"type":510,"value":718},"\u002Fusers\u002F123",{"type":510,"value":549},{"type":504,"tag":575,"props":721,"children":722},{},[723,728,730],{"type":504,"tag":520,"props":724,"children":725},{},[726],{"type":510,"value":727},"HTTP 动词 (Verbs)",{"type":510,"value":729},"：使用标准的 HTTP 方法来操作资源。\n",{"type":504,"tag":692,"props":731,"children":732},{},[733,744,755,766,777,788],{"type":504,"tag":575,"props":734,"children":735},{},[736,742],{"type":504,"tag":558,"props":737,"children":739},{"className":738},[],[740],{"type":510,"value":741},"GET \u002Fusers",{"type":510,"value":743},": 获取用户列表",{"type":504,"tag":575,"props":745,"children":746},{},[747,753],{"type":504,"tag":558,"props":748,"children":750},{"className":749},[],[751],{"type":510,"value":752},"GET \u002Fusers\u002F123",{"type":510,"value":754},": 获取单个用户",{"type":504,"tag":575,"props":756,"children":757},{},[758,764],{"type":504,"tag":558,"props":759,"children":761},{"className":760},[],[762],{"type":510,"value":763},"POST \u002Fusers",{"type":510,"value":765},": 创建一个新用户",{"type":504,"tag":575,"props":767,"children":768},{},[769,775],{"type":504,"tag":558,"props":770,"children":772},{"className":771},[],[773],{"type":510,"value":774},"PUT \u002Fusers\u002F123",{"type":510,"value":776},": 完整替换一个用户",{"type":504,"tag":575,"props":778,"children":779},{},[780,786],{"type":504,"tag":558,"props":781,"children":783},{"className":782},[],[784],{"type":510,"value":785},"PATCH \u002Fusers\u002F123",{"type":510,"value":787},": 部分更新一个用户",{"type":504,"tag":575,"props":789,"children":790},{},[791,797],{"type":504,"tag":558,"props":792,"children":794},{"className":793},[],[795],{"type":510,"value":796},"DELETE \u002Fusers\u002F123",{"type":510,"value":798},": 删除一个用户",{"type":504,"tag":575,"props":800,"children":801},{},[802,807,809],{"type":504,"tag":520,"props":803,"children":804},{},[805],{"type":510,"value":806},"状态码 (Status Codes)",{"type":510,"value":808},"：使用标准的 HTTP 状态码来表示请求结果。\n",{"type":504,"tag":692,"props":810,"children":811},{},[812,843,881],{"type":504,"tag":575,"props":813,"children":814},{},[815,821,823,829,830,836,837],{"type":504,"tag":558,"props":816,"children":818},{"className":817},[],[819],{"type":510,"value":820},"2xx",{"type":510,"value":822}," (成功): ",{"type":504,"tag":558,"props":824,"children":826},{"className":825},[],[827],{"type":510,"value":828},"200 OK",{"type":510,"value":712},{"type":504,"tag":558,"props":831,"children":833},{"className":832},[],[834],{"type":510,"value":835},"201 Created",{"type":510,"value":712},{"type":504,"tag":558,"props":838,"children":840},{"className":839},[],[841],{"type":510,"value":842},"204 No Content",{"type":504,"tag":575,"props":844,"children":845},{},[846,852,854,860,861,867,868,874,875],{"type":504,"tag":558,"props":847,"children":849},{"className":848},[],[850],{"type":510,"value":851},"4xx",{"type":510,"value":853}," (客户端错误): ",{"type":504,"tag":558,"props":855,"children":857},{"className":856},[],[858],{"type":510,"value":859},"400 Bad Request",{"type":510,"value":712},{"type":504,"tag":558,"props":862,"children":864},{"className":863},[],[865],{"type":510,"value":866},"401 Unauthorized",{"type":510,"value":712},{"type":504,"tag":558,"props":869,"children":871},{"className":870},[],[872],{"type":510,"value":873},"403 Forbidden",{"type":510,"value":712},{"type":504,"tag":558,"props":876,"children":878},{"className":877},[],[879],{"type":510,"value":880},"404 Not Found",{"type":504,"tag":575,"props":882,"children":883},{},[884,890,892],{"type":504,"tag":558,"props":885,"children":887},{"className":886},[],[888],{"type":510,"value":889},"5xx",{"type":510,"value":891}," (服务器错误): ",{"type":504,"tag":558,"props":893,"children":895},{"className":894},[],[896],{"type":510,"value":897},"500 Internal Server Error",{"type":504,"tag":564,"props":899,"children":901},{"id":900},"b-graphql",[902],{"type":510,"value":903},"B. GraphQL",{"type":504,"tag":516,"props":905,"children":906},{},[907],{"type":510,"value":908},"GraphQL 是一种用于 API 的查询语言和运行时。",{"type":504,"tag":692,"props":910,"children":911},{},[912,922,932],{"type":504,"tag":575,"props":913,"children":914},{},[915,920],{"type":504,"tag":520,"props":916,"children":917},{},[918],{"type":510,"value":919},"强类型",{"type":510,"value":921},"：所有 API 都通过一个 schema 来定义，类型安全。",{"type":504,"tag":575,"props":923,"children":924},{},[925,930],{"type":504,"tag":520,"props":926,"children":927},{},[928],{"type":510,"value":929},"客户端决定数据",{"type":510,"value":931},"：客户端可以精确地指定它需要哪些数据，不多也不少，解决了 REST 中 over-fetching 和 under-fetching 的问题。",{"type":504,"tag":575,"props":933,"children":934},{},[935,940,942,948,950,956],{"type":504,"tag":520,"props":936,"children":937},{},[938],{"type":510,"value":939},"单一端点",{"type":510,"value":941},"：通常只有一个端点（如 ",{"type":504,"tag":558,"props":943,"children":945},{"className":944},[],[946],{"type":510,"value":947},"\u002Fgraphql",{"type":510,"value":949},"），所有请求都通过 ",{"type":504,"tag":558,"props":951,"children":953},{"className":952},[],[954],{"type":510,"value":955},"POST",{"type":510,"value":957}," 发送到这里。",{"type":504,"tag":516,"props":959,"children":960},{},[961,966],{"type":504,"tag":520,"props":962,"children":963},{},[964],{"type":510,"value":965},"REST vs. GraphQL",{"type":510,"value":967},"：",{"type":504,"tag":692,"props":969,"children":970},{},[971,981],{"type":504,"tag":575,"props":972,"children":973},{},[974,979],{"type":504,"tag":520,"props":975,"children":976},{},[977],{"type":510,"value":978},"REST",{"type":510,"value":980}," 更简单，更符合 HTTP 语义，缓存机制更成熟。适合资源结构相对固定的场景。",{"type":504,"tag":575,"props":982,"children":983},{},[984,989],{"type":504,"tag":520,"props":985,"children":986},{},[987],{"type":510,"value":988},"GraphQL",{"type":510,"value":990}," 更灵活，更高效，尤其适合移动端或前端需求多变的复杂应用。",{"type":504,"tag":564,"props":992,"children":994},{"id":993},"c-一致的响应结构",[995],{"type":510,"value":996},"C. 一致的响应结构",{"type":504,"tag":516,"props":998,"children":999},{},[1000],{"type":510,"value":1001},"无论使用哪种规范，都应该定义一个全局一致的 JSON 响应结构，特别是对于错误响应。",{"type":504,"tag":516,"props":1003,"children":1004},{},[1005,1010],{"type":504,"tag":520,"props":1006,"children":1007},{},[1008],{"type":510,"value":1009},"成功响应",{"type":510,"value":967},{"type":504,"tag":551,"props":1012,"children":1016},{"className":1013,"code":1014,"language":1015,"meta":560,"style":560},"language-json shiki shiki-themes github-light github-dark","{\n  \"data\": {\n    \"id\": 123,\n    \"name\": \"John Doe\"\n  },\n  \"success\": true\n}\n","json",[1017],{"type":504,"tag":558,"props":1018,"children":1019},{"__ignoreMap":560},[1020,1032,1047,1071,1090,1099,1117],{"type":504,"tag":1021,"props":1022,"children":1025},"span",{"class":1023,"line":1024},"line",1,[1026],{"type":504,"tag":1021,"props":1027,"children":1029},{"style":1028},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[1030],{"type":510,"value":1031},"{\n",{"type":504,"tag":1021,"props":1033,"children":1035},{"class":1023,"line":1034},2,[1036,1042],{"type":504,"tag":1021,"props":1037,"children":1039},{"style":1038},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[1040],{"type":510,"value":1041},"  \"data\"",{"type":504,"tag":1021,"props":1043,"children":1044},{"style":1028},[1045],{"type":510,"value":1046},": {\n",{"type":504,"tag":1021,"props":1048,"children":1050},{"class":1023,"line":1049},3,[1051,1056,1061,1066],{"type":504,"tag":1021,"props":1052,"children":1053},{"style":1038},[1054],{"type":510,"value":1055},"    \"id\"",{"type":504,"tag":1021,"props":1057,"children":1058},{"style":1028},[1059],{"type":510,"value":1060},": ",{"type":504,"tag":1021,"props":1062,"children":1063},{"style":1038},[1064],{"type":510,"value":1065},"123",{"type":504,"tag":1021,"props":1067,"children":1068},{"style":1028},[1069],{"type":510,"value":1070},",\n",{"type":504,"tag":1021,"props":1072,"children":1074},{"class":1023,"line":1073},4,[1075,1080,1084],{"type":504,"tag":1021,"props":1076,"children":1077},{"style":1038},[1078],{"type":510,"value":1079},"    \"name\"",{"type":504,"tag":1021,"props":1081,"children":1082},{"style":1028},[1083],{"type":510,"value":1060},{"type":504,"tag":1021,"props":1085,"children":1087},{"style":1086},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[1088],{"type":510,"value":1089},"\"John Doe\"\n",{"type":504,"tag":1021,"props":1091,"children":1093},{"class":1023,"line":1092},5,[1094],{"type":504,"tag":1021,"props":1095,"children":1096},{"style":1028},[1097],{"type":510,"value":1098},"  },\n",{"type":504,"tag":1021,"props":1100,"children":1102},{"class":1023,"line":1101},6,[1103,1108,1112],{"type":504,"tag":1021,"props":1104,"children":1105},{"style":1038},[1106],{"type":510,"value":1107},"  \"success\"",{"type":504,"tag":1021,"props":1109,"children":1110},{"style":1028},[1111],{"type":510,"value":1060},{"type":504,"tag":1021,"props":1113,"children":1114},{"style":1038},[1115],{"type":510,"value":1116},"true\n",{"type":504,"tag":1021,"props":1118,"children":1120},{"class":1023,"line":1119},7,[1121],{"type":504,"tag":1021,"props":1122,"children":1123},{"style":1028},[1124],{"type":510,"value":1125},"}\n",{"type":504,"tag":516,"props":1127,"children":1128},{},[1129,1134],{"type":504,"tag":520,"props":1130,"children":1131},{},[1132],{"type":510,"value":1133},"错误响应",{"type":510,"value":967},{"type":504,"tag":551,"props":1136,"children":1138},{"className":1013,"code":1137,"language":1015,"meta":560,"style":560},"{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"Email address is not valid.\"\n  }\n}\n",[1139],{"type":504,"tag":558,"props":1140,"children":1141},{"__ignoreMap":560},[1142,1149,1169,1181,1202,1219,1227],{"type":504,"tag":1021,"props":1143,"children":1144},{"class":1023,"line":1024},[1145],{"type":504,"tag":1021,"props":1146,"children":1147},{"style":1028},[1148],{"type":510,"value":1031},{"type":504,"tag":1021,"props":1150,"children":1151},{"class":1023,"line":1034},[1152,1156,1160,1165],{"type":504,"tag":1021,"props":1153,"children":1154},{"style":1038},[1155],{"type":510,"value":1107},{"type":504,"tag":1021,"props":1157,"children":1158},{"style":1028},[1159],{"type":510,"value":1060},{"type":504,"tag":1021,"props":1161,"children":1162},{"style":1038},[1163],{"type":510,"value":1164},"false",{"type":504,"tag":1021,"props":1166,"children":1167},{"style":1028},[1168],{"type":510,"value":1070},{"type":504,"tag":1021,"props":1170,"children":1171},{"class":1023,"line":1049},[1172,1177],{"type":504,"tag":1021,"props":1173,"children":1174},{"style":1038},[1175],{"type":510,"value":1176},"  \"error\"",{"type":504,"tag":1021,"props":1178,"children":1179},{"style":1028},[1180],{"type":510,"value":1046},{"type":504,"tag":1021,"props":1182,"children":1183},{"class":1023,"line":1073},[1184,1189,1193,1198],{"type":504,"tag":1021,"props":1185,"children":1186},{"style":1038},[1187],{"type":510,"value":1188},"    \"code\"",{"type":504,"tag":1021,"props":1190,"children":1191},{"style":1028},[1192],{"type":510,"value":1060},{"type":504,"tag":1021,"props":1194,"children":1195},{"style":1086},[1196],{"type":510,"value":1197},"\"INVALID_INPUT\"",{"type":504,"tag":1021,"props":1199,"children":1200},{"style":1028},[1201],{"type":510,"value":1070},{"type":504,"tag":1021,"props":1203,"children":1204},{"class":1023,"line":1092},[1205,1210,1214],{"type":504,"tag":1021,"props":1206,"children":1207},{"style":1038},[1208],{"type":510,"value":1209},"    \"message\"",{"type":504,"tag":1021,"props":1211,"children":1212},{"style":1028},[1213],{"type":510,"value":1060},{"type":504,"tag":1021,"props":1215,"children":1216},{"style":1086},[1217],{"type":510,"value":1218},"\"Email address is not valid.\"\n",{"type":504,"tag":1021,"props":1220,"children":1221},{"class":1023,"line":1101},[1222],{"type":504,"tag":1021,"props":1223,"children":1224},{"style":1028},[1225],{"type":510,"value":1226},"  }\n",{"type":504,"tag":1021,"props":1228,"children":1229},{"class":1023,"line":1119},[1230],{"type":504,"tag":1021,"props":1231,"children":1232},{"style":1028},[1233],{"type":510,"value":1125},{"type":504,"tag":516,"props":1235,"children":1236},{},[1237],{"type":510,"value":1238},"这种一致性使得在前端封装统一的请求客户端和错误处理器变得非常容易。",{"type":504,"tag":528,"props":1240,"children":1241},{},[],{"type":504,"tag":532,"props":1243,"children":1245},{"id":1244},"_3-鉴权-authentication",[1246],{"type":510,"value":1247},"3. 鉴权 (Authentication)",{"type":504,"tag":516,"props":1249,"children":1250},{},[1251],{"type":510,"value":1252},"API 的鉴权模式必须清晰且统一。",{"type":504,"tag":692,"props":1254,"children":1255},{},[1256,1266,1276],{"type":504,"tag":575,"props":1257,"children":1258},{},[1259,1264],{"type":504,"tag":520,"props":1260,"children":1261},{},[1262],{"type":510,"value":1263},"公开端点 (Public)",{"type":510,"value":1265},"：无需任何鉴权的端点。",{"type":504,"tag":575,"props":1267,"children":1268},{},[1269,1274],{"type":504,"tag":520,"props":1270,"children":1271},{},[1272],{"type":510,"value":1273},"认证端点 (Authenticated)",{"type":510,"value":1275},"：需要用户登录才能访问的端点。",{"type":504,"tag":575,"props":1277,"children":1278},{},[1279,1284,1286,1292],{"type":504,"tag":520,"props":1280,"children":1281},{},[1282],{"type":510,"value":1283},"授权 (Authorization)",{"type":510,"value":1285},"：即使用户已登录，也需要检查其是否有权限访问特定资源（例如，一个普通用户不能访问管理员才能访问的 ",{"type":504,"tag":558,"props":1287,"children":1289},{"className":1288},[],[1290],{"type":510,"value":1291},"\u002Fadmin\u002Fdashboard",{"type":510,"value":1293}," API）。",{"type":504,"tag":516,"props":1295,"children":1296},{},[1297,1299,1305],{"type":510,"value":1298},"如上一篇文档所述，基于 JWT 的 Bearer Token 方案是目前的主流。所有需要认证的端点都应该校验 ",{"type":504,"tag":558,"props":1300,"children":1302},{"className":1301},[],[1303],{"type":510,"value":1304},"Authorization",{"type":510,"value":1306}," 头中的 JWT，并从中解析出用户 ID。",{"type":504,"tag":528,"props":1308,"children":1309},{},[],{"type":504,"tag":532,"props":1311,"children":1313},{"id":1312},"_4-缓存-caching",[1314],{"type":510,"value":1315},"4. 缓存 (Caching)",{"type":504,"tag":516,"props":1317,"children":1318},{},[1319],{"type":510,"value":1320},"高效的 API 缓存是提升性能、降低服务器成本的关键。",{"type":504,"tag":564,"props":1322,"children":1324},{"id":1323},"a-http-缓存-用于-restful-api",[1325],{"type":510,"value":1326},"A. HTTP 缓存 (用于 RESTful API)",{"type":504,"tag":692,"props":1328,"children":1329},{},[1330,1389],{"type":504,"tag":575,"props":1331,"children":1332},{},[1333,1344,1346],{"type":504,"tag":520,"props":1334,"children":1335},{},[1336,1342],{"type":504,"tag":558,"props":1337,"children":1339},{"className":1338},[],[1340],{"type":510,"value":1341},"Cache-Control",{"type":510,"value":1343}," 头",{"type":510,"value":1345},"：\n",{"type":504,"tag":692,"props":1347,"children":1348},{},[1349,1370],{"type":504,"tag":575,"props":1350,"children":1351},{},[1352,1354,1360,1362,1368],{"type":510,"value":1353},"对于不经常变化的 ",{"type":504,"tag":558,"props":1355,"children":1357},{"className":1356},[],[1358],{"type":510,"value":1359},"GET",{"type":510,"value":1361}," 请求（如获取配置信息、商品列表），可以设置一个较短的缓存时间：",{"type":504,"tag":558,"props":1363,"children":1365},{"className":1364},[],[1366],{"type":510,"value":1367},"Cache-Control: public, max-age=60",{"type":510,"value":1369},"（缓存 60 秒）。",{"type":504,"tag":575,"props":1371,"children":1372},{},[1373,1375,1381,1382,1388],{"type":510,"value":1374},"对于用户特定的数据，使用 ",{"type":504,"tag":558,"props":1376,"children":1378},{"className":1377},[],[1379],{"type":510,"value":1380},"private",{"type":510,"value":1060},{"type":504,"tag":558,"props":1383,"children":1385},{"className":1384},[],[1386],{"type":510,"value":1387},"Cache-Control: private, max-age=60",{"type":510,"value":549},{"type":504,"tag":575,"props":1390,"children":1391},{},[1392,1409,1410],{"type":504,"tag":520,"props":1393,"children":1394},{},[1395,1401,1403],{"type":504,"tag":558,"props":1396,"children":1398},{"className":1397},[],[1399],{"type":510,"value":1400},"ETag",{"type":510,"value":1402}," \u002F ",{"type":504,"tag":558,"props":1404,"children":1406},{"className":1405},[],[1407],{"type":510,"value":1408},"Last-Modified",{"type":510,"value":1345},{"type":504,"tag":692,"props":1411,"children":1412},{},[1413,1425,1438],{"type":504,"tag":575,"props":1414,"children":1415},{},[1416,1418,1423],{"type":510,"value":1417},"服务器为资源生成一个 ",{"type":504,"tag":558,"props":1419,"children":1421},{"className":1420},[],[1422],{"type":510,"value":1400},{"type":510,"value":1424}," (内容的 hash)。",{"type":504,"tag":575,"props":1426,"children":1427},{},[1428,1430,1436],{"type":510,"value":1429},"客户端在下一次请求时，带上 ",{"type":504,"tag":558,"props":1431,"children":1433},{"className":1432},[],[1434],{"type":510,"value":1435},"If-None-Match: \u003CETag>",{"type":510,"value":1437}," 头。",{"type":504,"tag":575,"props":1439,"children":1440},{},[1441,1443,1449],{"type":510,"value":1442},"如果服务器发现内容未变，返回一个空的 ",{"type":504,"tag":558,"props":1444,"children":1446},{"className":1445},[],[1447],{"type":510,"value":1448},"304 Not Modified",{"type":510,"value":1450}," 响应，客户端则使用本地缓存。这极大地节省了带宽。",{"type":504,"tag":564,"props":1452,"children":1454},{"id":1453},"b-应用层缓存",[1455],{"type":510,"value":1456},"B. 应用层缓存",{"type":504,"tag":692,"props":1458,"children":1459},{},[1460,1477],{"type":504,"tag":575,"props":1461,"children":1462},{},[1463,1468,1470,1475],{"type":504,"tag":520,"props":1464,"children":1465},{},[1466],{"type":510,"value":1467},"客户端缓存",{"type":510,"value":1469},"：像 ",{"type":504,"tag":520,"props":1471,"children":1472},{},[1473],{"type":510,"value":1474},"TanStack Query",{"type":510,"value":1476}," 这样的库，在客户端内存中提供了强大的 API 缓存能力（缓存、失效、后台刷新）。这是提升前端感知性能的最直接手段。",{"type":504,"tag":575,"props":1478,"children":1479},{},[1480,1485,1487,1492],{"type":504,"tag":520,"props":1481,"children":1482},{},[1483],{"type":510,"value":1484},"BFF\u002F服务器端缓存",{"type":510,"value":1486},"：对于一些计算成本高、但结果相对稳定的 API，可以在 BFF 或后端服务中使用 ",{"type":504,"tag":520,"props":1488,"children":1489},{},[1490],{"type":510,"value":1491},"Redis",{"type":510,"value":1493}," 等内存数据库进行缓存。",{"type":504,"tag":516,"props":1495,"children":1496},{},[1497,1502],{"type":504,"tag":520,"props":1498,"children":1499},{},[1500],{"type":510,"value":1501},"缓存策略",{"type":510,"value":1503},"：需要根据数据的特性来决定。是所有用户共享的数据，还是用户私有的数据？数据的更新频率如何？可以接受多大程度的延迟（stale）？",{"type":504,"tag":528,"props":1505,"children":1506},{},[],{"type":504,"tag":532,"props":1508,"children":1510},{"id":1509},"_5-关联阅读",[1511],{"type":510,"value":1512},"5. 关联阅读",{"type":504,"tag":692,"props":1514,"children":1515},{},[1516,1534,1557,1573],{"type":504,"tag":575,"props":1517,"children":1518},{},[1519,1524,1525],{"type":504,"tag":520,"props":1520,"children":1521},{},[1522],{"type":510,"value":1523},"Microsoft aPaaS 文档",{"type":510,"value":1060},{"type":504,"tag":1526,"props":1527,"children":1531},"a",{"href":1528,"rel":1529},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fazure\u002Farchitecture\u002Fpatterns\u002Fbackends-for-frontends",[1530],"nofollow",[1532],{"type":510,"value":1533},"Backend for Frontend pattern",{"type":504,"tag":575,"props":1535,"children":1536},{},[1537,1542,1543,1549,1550],{"type":504,"tag":520,"props":1538,"children":1539},{},[1540],{"type":510,"value":1541},"MDN 文档",{"type":510,"value":1060},{"type":504,"tag":1526,"props":1544,"children":1547},{"href":1545,"rel":1546},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FStatus",[1530],[1548],{"type":510,"value":978},{"type":510,"value":712},{"type":504,"tag":1526,"props":1551,"children":1554},{"href":1552,"rel":1553},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCaching",[1530],[1555],{"type":510,"value":1556},"HTTP Caching",{"type":504,"tag":575,"props":1558,"children":1559},{},[1560,1565,1566],{"type":504,"tag":520,"props":1561,"children":1562},{},[1563],{"type":510,"value":1564},"GraphQL 官网",{"type":510,"value":1060},{"type":504,"tag":1526,"props":1567,"children":1570},{"href":1568,"rel":1569},"https:\u002F\u002Fgraphql.org\u002F",[1530],[1571],{"type":510,"value":1572},"graphql.org",{"type":504,"tag":575,"props":1574,"children":1575},{},[1576,1581,1583],{"type":504,"tag":520,"props":1577,"children":1578},{},[1579],{"type":510,"value":1580},"RESTful API 设计指南",{"type":510,"value":1582}," by 阮一峰: ",{"type":504,"tag":1526,"props":1584,"children":1587},{"href":1585,"rel":1586},"https:\u002F\u002Fwww.ruanyifeng.com\u002Fblog\u002F2014\u002F05\u002Frestful_api.html",[1530],[1588],{"type":510,"value":1589},"ruanyifeng.com\u002Fblog\u002F2014\u002F05\u002Frestful_api.html",{"type":504,"tag":1591,"props":1592,"children":1593},"style",{},[1594],{"type":510,"value":1595},"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":560,"searchDepth":1034,"depth":1034,"links":1597},[1598,1602,1607,1608,1612],{"id":534,"depth":1034,"text":537,"children":1599},[1600,1601],{"id":566,"depth":1049,"text":569},{"id":627,"depth":1049,"text":630},{"id":671,"depth":1034,"text":674,"children":1603},[1604,1605,1606],{"id":682,"depth":1049,"text":685},{"id":900,"depth":1049,"text":903},{"id":993,"depth":1049,"text":996},{"id":1244,"depth":1034,"text":1247},{"id":1312,"depth":1034,"text":1315,"children":1609},[1610,1611],{"id":1323,"depth":1049,"text":1326},{"id":1453,"depth":1049,"text":1456},{"id":1509,"depth":1034,"text":1512},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1614},"\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":1616},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1618},"\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":1620,"height":1620,"rotate":4,"vFlip":6,"hFlip":6,"body":1621},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>"]