[{"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-16-advanced\u002F03-realtime-and-offline":494,"i-ri:arrow-left-s-line":2935,"i-ri:menu-2-line":2937,"i-ri:arrow-right-s-line":2939,"i-ep:copy-document":2941},{"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":483,"path":483,"title":484,"updatedAt":21,"meta":498,"body":500,"toc":2923},"bm90ZTpub3Rlcy9yZWFjdC8xNi1hZHZhbmNlZC8wMy1yZWFsdGltZS1hbmQtb2ZmbGluZS5tZA","note","notes\u002Freact\u002F16-advanced\u002F03-realtime-and-offline.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":484,"updatedAt":21,"lastUpdated":21},"react\u002F16-advanced\u002F03-realtime-and-offline",{"type":501,"children":502},"root",[503,511,527,531,538,543,550,562,642,652,1338,1344,1356,1442,1451,1873,1876,1882,1888,1893,1903,1937,1943,1954,2053,2069,2563,2572,2774,2780,2792,2830,2833,2839,2917],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"实时与离线websocket-pwa-与-service-worker",[509],{"type":510,"value":484},"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},"：通过 WebSocket 或 SSE 赋予应用实时通信的能力，并通过 Service Worker 缓存机制和 PWA 标准构建可离线访问、媲美原生体验的 Web 应用，是现代前端突破传统请求-响应模型、迈向更高用户体验维度的关键技术。",{"type":504,"tag":528,"props":529,"children":530},"hr",{},[],{"type":504,"tag":532,"props":533,"children":535},"h2",{"id":534},"_1-实时通信-real-time-communication",[536],{"type":510,"value":537},"1. 实时通信 (Real-time Communication)",{"type":504,"tag":516,"props":539,"children":540},{},[541],{"type":510,"value":542},"当应用需要即时地从服务器接收更新（如聊天消息、通知、实时股价）时，传统的轮询 (polling) 方式效率低下且延迟高。",{"type":504,"tag":544,"props":545,"children":547},"h3",{"id":546},"a-websocket",[548],{"type":510,"value":549},"A. WebSocket",{"type":504,"tag":516,"props":551,"children":552},{},[553,555,560],{"type":510,"value":554},"WebSocket 提供了一个在单个 TCP 连接上进行",{"type":504,"tag":520,"props":556,"children":557},{},[558],{"type":510,"value":559},"全双工、双向通信",{"type":510,"value":561},"的协议。一旦连接建立，客户端和服务器都可以随时向对方发送数据。",{"type":504,"tag":563,"props":564,"children":565},"ul",{},[566,610,632],{"type":504,"tag":567,"props":568,"children":569},"li",{},[570,575,577],{"type":504,"tag":520,"props":571,"children":572},{},[573],{"type":510,"value":574},"优点",{"type":510,"value":576},"：\n",{"type":504,"tag":563,"props":578,"children":579},{},[580,590,600],{"type":504,"tag":567,"props":581,"children":582},{},[583,588],{"type":504,"tag":520,"props":584,"children":585},{},[586],{"type":510,"value":587},"真正的双向通信",{"type":510,"value":589},"：客户端和服务器地位平等。",{"type":504,"tag":567,"props":591,"children":592},{},[593,598],{"type":504,"tag":520,"props":594,"children":595},{},[596],{"type":510,"value":597},"低延迟",{"type":510,"value":599},"：连接开销小，数据传输快。",{"type":504,"tag":567,"props":601,"children":602},{},[603,608],{"type":504,"tag":520,"props":604,"children":605},{},[606],{"type":510,"value":607},"性能高",{"type":510,"value":609},"：HTTP 头部开销非常小。",{"type":504,"tag":567,"props":611,"children":612},{},[613,618,619],{"type":504,"tag":520,"props":614,"children":615},{},[616],{"type":510,"value":617},"缺点",{"type":510,"value":576},{"type":504,"tag":563,"props":620,"children":621},{},[622,627],{"type":504,"tag":567,"props":623,"children":624},{},[625],{"type":510,"value":626},"需要专门的服务器支持。",{"type":504,"tag":567,"props":628,"children":629},{},[630],{"type":510,"value":631},"在某些严格的网络环境下可能会被防火墙阻止。",{"type":504,"tag":567,"props":633,"children":634},{},[635,640],{"type":504,"tag":520,"props":636,"children":637},{},[638],{"type":510,"value":639},"适用场景",{"type":510,"value":641},"：在线聊天、多人协作编辑、实时游戏、需要客户端频繁向服务器发送数据的场景。",{"type":504,"tag":516,"props":643,"children":644},{},[645,650],{"type":504,"tag":520,"props":646,"children":647},{},[648],{"type":510,"value":649},"在 React 中使用",{"type":510,"value":651},"：\n通常我们会封装一个自定义 Hook 来管理 WebSocket 连接的生命周期、状态和消息收发。",{"type":504,"tag":653,"props":654,"children":659},"pre",{"className":655,"code":656,"language":657,"meta":658,"style":658},"language-jsx shiki shiki-themes github-light github-dark","\u002F\u002F useWebSocket.js\nimport { useState, useEffect, useRef } from 'react';\n\nexport function useWebSocket(url) {\n  const [messages, setMessages] = useState([]);\n  const ws = useRef(null);\n\n  useEffect(() => {\n    ws.current = new WebSocket(url);\n    ws.current.onopen = () => console.log('WebSocket connected');\n    ws.current.onclose = () => console.log('WebSocket disconnected');\n\n    ws.current.onmessage = (event) => {\n      setMessages(prev => [...prev, JSON.parse(event.data)]);\n    };\n\n    return () => {\n      ws.current.close();\n    };\n  }, [url]);\n\n  const sendMessage = (message) => {\n    if (ws.current.readyState === WebSocket.OPEN) {\n      ws.current.send(JSON.stringify(message));\n    }\n  };\n\n  return { messages, sendMessage };\n}\n","jsx","",[660],{"type":504,"tag":661,"props":662,"children":663},"code",{"__ignoreMap":658},[664,676,708,718,754,804,841,849,873,901,951,997,1005,1045,1102,1111,1119,1140,1159,1167,1176,1184,1222,1255,1289,1298,1307,1315,1329],{"type":504,"tag":665,"props":666,"children":669},"span",{"class":667,"line":668},"line",1,[670],{"type":504,"tag":665,"props":671,"children":673},{"style":672},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[674],{"type":510,"value":675},"\u002F\u002F useWebSocket.js\n",{"type":504,"tag":665,"props":677,"children":679},{"class":667,"line":678},2,[680,686,692,697,703],{"type":504,"tag":665,"props":681,"children":683},{"style":682},"--shiki-default:#D73A49;--shiki-dark:#F97583",[684],{"type":510,"value":685},"import",{"type":504,"tag":665,"props":687,"children":689},{"style":688},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[690],{"type":510,"value":691}," { useState, useEffect, useRef } ",{"type":504,"tag":665,"props":693,"children":694},{"style":682},[695],{"type":510,"value":696},"from",{"type":504,"tag":665,"props":698,"children":700},{"style":699},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[701],{"type":510,"value":702}," 'react'",{"type":504,"tag":665,"props":704,"children":705},{"style":688},[706],{"type":510,"value":707},";\n",{"type":504,"tag":665,"props":709,"children":711},{"class":667,"line":710},3,[712],{"type":504,"tag":665,"props":713,"children":715},{"emptyLinePlaceholder":714},true,[716],{"type":510,"value":717},"\n",{"type":504,"tag":665,"props":719,"children":721},{"class":667,"line":720},4,[722,727,732,738,743,749],{"type":504,"tag":665,"props":723,"children":724},{"style":682},[725],{"type":510,"value":726},"export",{"type":504,"tag":665,"props":728,"children":729},{"style":682},[730],{"type":510,"value":731}," function",{"type":504,"tag":665,"props":733,"children":735},{"style":734},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[736],{"type":510,"value":737}," useWebSocket",{"type":504,"tag":665,"props":739,"children":740},{"style":688},[741],{"type":510,"value":742},"(",{"type":504,"tag":665,"props":744,"children":746},{"style":745},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[747],{"type":510,"value":748},"url",{"type":504,"tag":665,"props":750,"children":751},{"style":688},[752],{"type":510,"value":753},") {\n",{"type":504,"tag":665,"props":755,"children":757},{"class":667,"line":756},5,[758,763,768,774,779,784,789,794,799],{"type":504,"tag":665,"props":759,"children":760},{"style":682},[761],{"type":510,"value":762},"  const",{"type":504,"tag":665,"props":764,"children":765},{"style":688},[766],{"type":510,"value":767}," [",{"type":504,"tag":665,"props":769,"children":771},{"style":770},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[772],{"type":510,"value":773},"messages",{"type":504,"tag":665,"props":775,"children":776},{"style":688},[777],{"type":510,"value":778},", ",{"type":504,"tag":665,"props":780,"children":781},{"style":770},[782],{"type":510,"value":783},"setMessages",{"type":504,"tag":665,"props":785,"children":786},{"style":688},[787],{"type":510,"value":788},"] ",{"type":504,"tag":665,"props":790,"children":791},{"style":682},[792],{"type":510,"value":793},"=",{"type":504,"tag":665,"props":795,"children":796},{"style":734},[797],{"type":510,"value":798}," useState",{"type":504,"tag":665,"props":800,"children":801},{"style":688},[802],{"type":510,"value":803},"([]);\n",{"type":504,"tag":665,"props":805,"children":807},{"class":667,"line":806},6,[808,812,817,822,827,831,836],{"type":504,"tag":665,"props":809,"children":810},{"style":682},[811],{"type":510,"value":762},{"type":504,"tag":665,"props":813,"children":814},{"style":770},[815],{"type":510,"value":816}," ws",{"type":504,"tag":665,"props":818,"children":819},{"style":682},[820],{"type":510,"value":821}," =",{"type":504,"tag":665,"props":823,"children":824},{"style":734},[825],{"type":510,"value":826}," useRef",{"type":504,"tag":665,"props":828,"children":829},{"style":688},[830],{"type":510,"value":742},{"type":504,"tag":665,"props":832,"children":833},{"style":770},[834],{"type":510,"value":835},"null",{"type":504,"tag":665,"props":837,"children":838},{"style":688},[839],{"type":510,"value":840},");\n",{"type":504,"tag":665,"props":842,"children":844},{"class":667,"line":843},7,[845],{"type":504,"tag":665,"props":846,"children":847},{"emptyLinePlaceholder":714},[848],{"type":510,"value":717},{"type":504,"tag":665,"props":850,"children":852},{"class":667,"line":851},8,[853,858,863,868],{"type":504,"tag":665,"props":854,"children":855},{"style":734},[856],{"type":510,"value":857},"  useEffect",{"type":504,"tag":665,"props":859,"children":860},{"style":688},[861],{"type":510,"value":862},"(() ",{"type":504,"tag":665,"props":864,"children":865},{"style":682},[866],{"type":510,"value":867},"=>",{"type":504,"tag":665,"props":869,"children":870},{"style":688},[871],{"type":510,"value":872}," {\n",{"type":504,"tag":665,"props":874,"children":876},{"class":667,"line":875},9,[877,882,886,891,896],{"type":504,"tag":665,"props":878,"children":879},{"style":688},[880],{"type":510,"value":881},"    ws.current ",{"type":504,"tag":665,"props":883,"children":884},{"style":682},[885],{"type":510,"value":793},{"type":504,"tag":665,"props":887,"children":888},{"style":682},[889],{"type":510,"value":890}," new",{"type":504,"tag":665,"props":892,"children":893},{"style":734},[894],{"type":510,"value":895}," WebSocket",{"type":504,"tag":665,"props":897,"children":898},{"style":688},[899],{"type":510,"value":900},"(url);\n",{"type":504,"tag":665,"props":902,"children":904},{"class":667,"line":903},10,[905,910,915,919,924,928,933,938,942,947],{"type":504,"tag":665,"props":906,"children":907},{"style":688},[908],{"type":510,"value":909},"    ws.current.",{"type":504,"tag":665,"props":911,"children":912},{"style":734},[913],{"type":510,"value":914},"onopen",{"type":504,"tag":665,"props":916,"children":917},{"style":682},[918],{"type":510,"value":821},{"type":504,"tag":665,"props":920,"children":921},{"style":688},[922],{"type":510,"value":923}," () ",{"type":504,"tag":665,"props":925,"children":926},{"style":682},[927],{"type":510,"value":867},{"type":504,"tag":665,"props":929,"children":930},{"style":688},[931],{"type":510,"value":932}," console.",{"type":504,"tag":665,"props":934,"children":935},{"style":734},[936],{"type":510,"value":937},"log",{"type":504,"tag":665,"props":939,"children":940},{"style":688},[941],{"type":510,"value":742},{"type":504,"tag":665,"props":943,"children":944},{"style":699},[945],{"type":510,"value":946},"'WebSocket connected'",{"type":504,"tag":665,"props":948,"children":949},{"style":688},[950],{"type":510,"value":840},{"type":504,"tag":665,"props":952,"children":954},{"class":667,"line":953},11,[955,959,964,968,972,976,980,984,988,993],{"type":504,"tag":665,"props":956,"children":957},{"style":688},[958],{"type":510,"value":909},{"type":504,"tag":665,"props":960,"children":961},{"style":734},[962],{"type":510,"value":963},"onclose",{"type":504,"tag":665,"props":965,"children":966},{"style":682},[967],{"type":510,"value":821},{"type":504,"tag":665,"props":969,"children":970},{"style":688},[971],{"type":510,"value":923},{"type":504,"tag":665,"props":973,"children":974},{"style":682},[975],{"type":510,"value":867},{"type":504,"tag":665,"props":977,"children":978},{"style":688},[979],{"type":510,"value":932},{"type":504,"tag":665,"props":981,"children":982},{"style":734},[983],{"type":510,"value":937},{"type":504,"tag":665,"props":985,"children":986},{"style":688},[987],{"type":510,"value":742},{"type":504,"tag":665,"props":989,"children":990},{"style":699},[991],{"type":510,"value":992},"'WebSocket disconnected'",{"type":504,"tag":665,"props":994,"children":995},{"style":688},[996],{"type":510,"value":840},{"type":504,"tag":665,"props":998,"children":1000},{"class":667,"line":999},12,[1001],{"type":504,"tag":665,"props":1002,"children":1003},{"emptyLinePlaceholder":714},[1004],{"type":510,"value":717},{"type":504,"tag":665,"props":1006,"children":1008},{"class":667,"line":1007},13,[1009,1013,1018,1022,1027,1032,1037,1041],{"type":504,"tag":665,"props":1010,"children":1011},{"style":688},[1012],{"type":510,"value":909},{"type":504,"tag":665,"props":1014,"children":1015},{"style":734},[1016],{"type":510,"value":1017},"onmessage",{"type":504,"tag":665,"props":1019,"children":1020},{"style":682},[1021],{"type":510,"value":821},{"type":504,"tag":665,"props":1023,"children":1024},{"style":688},[1025],{"type":510,"value":1026}," (",{"type":504,"tag":665,"props":1028,"children":1029},{"style":745},[1030],{"type":510,"value":1031},"event",{"type":504,"tag":665,"props":1033,"children":1034},{"style":688},[1035],{"type":510,"value":1036},") ",{"type":504,"tag":665,"props":1038,"children":1039},{"style":682},[1040],{"type":510,"value":867},{"type":504,"tag":665,"props":1042,"children":1043},{"style":688},[1044],{"type":510,"value":872},{"type":504,"tag":665,"props":1046,"children":1048},{"class":667,"line":1047},14,[1049,1054,1058,1063,1068,1072,1077,1082,1087,1092,1097],{"type":504,"tag":665,"props":1050,"children":1051},{"style":734},[1052],{"type":510,"value":1053},"      setMessages",{"type":504,"tag":665,"props":1055,"children":1056},{"style":688},[1057],{"type":510,"value":742},{"type":504,"tag":665,"props":1059,"children":1060},{"style":745},[1061],{"type":510,"value":1062},"prev",{"type":504,"tag":665,"props":1064,"children":1065},{"style":682},[1066],{"type":510,"value":1067}," =>",{"type":504,"tag":665,"props":1069,"children":1070},{"style":688},[1071],{"type":510,"value":767},{"type":504,"tag":665,"props":1073,"children":1074},{"style":682},[1075],{"type":510,"value":1076},"...",{"type":504,"tag":665,"props":1078,"children":1079},{"style":688},[1080],{"type":510,"value":1081},"prev, ",{"type":504,"tag":665,"props":1083,"children":1084},{"style":770},[1085],{"type":510,"value":1086},"JSON",{"type":504,"tag":665,"props":1088,"children":1089},{"style":688},[1090],{"type":510,"value":1091},".",{"type":504,"tag":665,"props":1093,"children":1094},{"style":734},[1095],{"type":510,"value":1096},"parse",{"type":504,"tag":665,"props":1098,"children":1099},{"style":688},[1100],{"type":510,"value":1101},"(event.data)]);\n",{"type":504,"tag":665,"props":1103,"children":1105},{"class":667,"line":1104},15,[1106],{"type":504,"tag":665,"props":1107,"children":1108},{"style":688},[1109],{"type":510,"value":1110},"    };\n",{"type":504,"tag":665,"props":1112,"children":1114},{"class":667,"line":1113},16,[1115],{"type":504,"tag":665,"props":1116,"children":1117},{"emptyLinePlaceholder":714},[1118],{"type":510,"value":717},{"type":504,"tag":665,"props":1120,"children":1122},{"class":667,"line":1121},17,[1123,1128,1132,1136],{"type":504,"tag":665,"props":1124,"children":1125},{"style":682},[1126],{"type":510,"value":1127},"    return",{"type":504,"tag":665,"props":1129,"children":1130},{"style":688},[1131],{"type":510,"value":923},{"type":504,"tag":665,"props":1133,"children":1134},{"style":682},[1135],{"type":510,"value":867},{"type":504,"tag":665,"props":1137,"children":1138},{"style":688},[1139],{"type":510,"value":872},{"type":504,"tag":665,"props":1141,"children":1143},{"class":667,"line":1142},18,[1144,1149,1154],{"type":504,"tag":665,"props":1145,"children":1146},{"style":688},[1147],{"type":510,"value":1148},"      ws.current.",{"type":504,"tag":665,"props":1150,"children":1151},{"style":734},[1152],{"type":510,"value":1153},"close",{"type":504,"tag":665,"props":1155,"children":1156},{"style":688},[1157],{"type":510,"value":1158},"();\n",{"type":504,"tag":665,"props":1160,"children":1162},{"class":667,"line":1161},19,[1163],{"type":504,"tag":665,"props":1164,"children":1165},{"style":688},[1166],{"type":510,"value":1110},{"type":504,"tag":665,"props":1168,"children":1170},{"class":667,"line":1169},20,[1171],{"type":504,"tag":665,"props":1172,"children":1173},{"style":688},[1174],{"type":510,"value":1175},"  }, [url]);\n",{"type":504,"tag":665,"props":1177,"children":1179},{"class":667,"line":1178},21,[1180],{"type":504,"tag":665,"props":1181,"children":1182},{"emptyLinePlaceholder":714},[1183],{"type":510,"value":717},{"type":504,"tag":665,"props":1185,"children":1187},{"class":667,"line":1186},22,[1188,1192,1197,1201,1205,1210,1214,1218],{"type":504,"tag":665,"props":1189,"children":1190},{"style":682},[1191],{"type":510,"value":762},{"type":504,"tag":665,"props":1193,"children":1194},{"style":734},[1195],{"type":510,"value":1196}," sendMessage",{"type":504,"tag":665,"props":1198,"children":1199},{"style":682},[1200],{"type":510,"value":821},{"type":504,"tag":665,"props":1202,"children":1203},{"style":688},[1204],{"type":510,"value":1026},{"type":504,"tag":665,"props":1206,"children":1207},{"style":745},[1208],{"type":510,"value":1209},"message",{"type":504,"tag":665,"props":1211,"children":1212},{"style":688},[1213],{"type":510,"value":1036},{"type":504,"tag":665,"props":1215,"children":1216},{"style":682},[1217],{"type":510,"value":867},{"type":504,"tag":665,"props":1219,"children":1220},{"style":688},[1221],{"type":510,"value":872},{"type":504,"tag":665,"props":1223,"children":1225},{"class":667,"line":1224},23,[1226,1231,1236,1241,1246,1251],{"type":504,"tag":665,"props":1227,"children":1228},{"style":682},[1229],{"type":510,"value":1230},"    if",{"type":504,"tag":665,"props":1232,"children":1233},{"style":688},[1234],{"type":510,"value":1235}," (ws.current.readyState ",{"type":504,"tag":665,"props":1237,"children":1238},{"style":682},[1239],{"type":510,"value":1240},"===",{"type":504,"tag":665,"props":1242,"children":1243},{"style":688},[1244],{"type":510,"value":1245}," WebSocket.",{"type":504,"tag":665,"props":1247,"children":1248},{"style":770},[1249],{"type":510,"value":1250},"OPEN",{"type":504,"tag":665,"props":1252,"children":1253},{"style":688},[1254],{"type":510,"value":753},{"type":504,"tag":665,"props":1256,"children":1257},{"class":667,"line":5},[1258,1262,1267,1271,1275,1279,1284],{"type":504,"tag":665,"props":1259,"children":1260},{"style":688},[1261],{"type":510,"value":1148},{"type":504,"tag":665,"props":1263,"children":1264},{"style":734},[1265],{"type":510,"value":1266},"send",{"type":504,"tag":665,"props":1268,"children":1269},{"style":688},[1270],{"type":510,"value":742},{"type":504,"tag":665,"props":1272,"children":1273},{"style":770},[1274],{"type":510,"value":1086},{"type":504,"tag":665,"props":1276,"children":1277},{"style":688},[1278],{"type":510,"value":1091},{"type":504,"tag":665,"props":1280,"children":1281},{"style":734},[1282],{"type":510,"value":1283},"stringify",{"type":504,"tag":665,"props":1285,"children":1286},{"style":688},[1287],{"type":510,"value":1288},"(message));\n",{"type":504,"tag":665,"props":1290,"children":1292},{"class":667,"line":1291},25,[1293],{"type":504,"tag":665,"props":1294,"children":1295},{"style":688},[1296],{"type":510,"value":1297},"    }\n",{"type":504,"tag":665,"props":1299,"children":1301},{"class":667,"line":1300},26,[1302],{"type":504,"tag":665,"props":1303,"children":1304},{"style":688},[1305],{"type":510,"value":1306},"  };\n",{"type":504,"tag":665,"props":1308,"children":1310},{"class":667,"line":1309},27,[1311],{"type":504,"tag":665,"props":1312,"children":1313},{"emptyLinePlaceholder":714},[1314],{"type":510,"value":717},{"type":504,"tag":665,"props":1316,"children":1318},{"class":667,"line":1317},28,[1319,1324],{"type":504,"tag":665,"props":1320,"children":1321},{"style":682},[1322],{"type":510,"value":1323},"  return",{"type":504,"tag":665,"props":1325,"children":1326},{"style":688},[1327],{"type":510,"value":1328}," { messages, sendMessage };\n",{"type":504,"tag":665,"props":1330,"children":1332},{"class":667,"line":1331},29,[1333],{"type":504,"tag":665,"props":1334,"children":1335},{"style":688},[1336],{"type":510,"value":1337},"}\n",{"type":504,"tag":544,"props":1339,"children":1341},{"id":1340},"b-server-sent-events-sse",[1342],{"type":510,"value":1343},"B. Server-Sent Events (SSE)",{"type":504,"tag":516,"props":1345,"children":1346},{},[1347,1349,1354],{"type":510,"value":1348},"SSE 是一个浏览器 API，它允许服务器",{"type":504,"tag":520,"props":1350,"children":1351},{},[1352],{"type":510,"value":1353},"单向地",{"type":510,"value":1355},"向客户端推送事件流。它建立在常规的 HTTP\u002FHTTPS 连接之上。",{"type":504,"tag":563,"props":1357,"children":1358},{},[1359,1400,1426],{"type":504,"tag":567,"props":1360,"children":1361},{},[1362,1366,1367],{"type":504,"tag":520,"props":1363,"children":1364},{},[1365],{"type":510,"value":574},{"type":510,"value":576},{"type":504,"tag":563,"props":1368,"children":1369},{},[1370,1380,1390],{"type":504,"tag":567,"props":1371,"children":1372},{},[1373,1378],{"type":504,"tag":520,"props":1374,"children":1375},{},[1376],{"type":510,"value":1377},"简单",{"type":510,"value":1379},"：基于标准 HTTP，无需特殊服务器，易于实现和调试。",{"type":504,"tag":567,"props":1381,"children":1382},{},[1383,1388],{"type":504,"tag":520,"props":1384,"children":1385},{},[1386],{"type":510,"value":1387},"自动重连",{"type":510,"value":1389},"：浏览器会自动处理断线重连。",{"type":504,"tag":567,"props":1391,"children":1392},{},[1393,1398],{"type":504,"tag":520,"props":1394,"children":1395},{},[1396],{"type":510,"value":1397},"兼容性好",{"type":510,"value":1399},"：能通过几乎所有的网络环境。",{"type":504,"tag":567,"props":1401,"children":1402},{},[1403,1407,1408],{"type":504,"tag":520,"props":1404,"children":1405},{},[1406],{"type":510,"value":617},{"type":510,"value":576},{"type":504,"tag":563,"props":1409,"children":1410},{},[1411,1421],{"type":504,"tag":567,"props":1412,"children":1413},{},[1414,1419],{"type":504,"tag":520,"props":1415,"children":1416},{},[1417],{"type":510,"value":1418},"单向通信",{"type":510,"value":1420},"：只能从服务器到客户端。",{"type":504,"tag":567,"props":1422,"children":1423},{},[1424],{"type":510,"value":1425},"有连接数限制（浏览器对同域名下的 HTTP 连接数有限制）。",{"type":504,"tag":567,"props":1427,"children":1428},{},[1429,1433,1435,1440],{"type":504,"tag":520,"props":1430,"children":1431},{},[1432],{"type":510,"value":639},{"type":510,"value":1434},"：新闻源更新、股票价格推送、通知提醒、构建状态更新等",{"type":504,"tag":520,"props":1436,"children":1437},{},[1438],{"type":510,"value":1439},"只读",{"type":510,"value":1441},"的实时数据流。",{"type":504,"tag":516,"props":1443,"children":1444},{},[1445,1449],{"type":504,"tag":520,"props":1446,"children":1447},{},[1448],{"type":510,"value":649},{"type":510,"value":1450},"：\n与 WebSocket 类似，可以封装一个 Hook 来处理。",{"type":504,"tag":653,"props":1452,"children":1454},{"className":655,"code":1453,"language":657,"meta":658,"style":658},"\u002F\u002F useEventSource.js\nimport { useState, useEffect } from 'react';\n\nexport function useEventSource(url) {\n  const [data, setData] = useState(null);\n\n  useEffect(() => {\n    const eventSource = new EventSource(url);\n    \n    \u002F\u002F 监听特定类型的事件\n    eventSource.addEventListener('update', (event) => {\n      setData(JSON.parse(event.data));\n    });\n\n    eventSource.onerror = () => {\n      \u002F\u002F 处理错误，可以关闭连接\n      eventSource.close();\n    };\n\n    return () => {\n      eventSource.close();\n    };\n  }, [url]);\n\n  return data;\n}\n",[1455],{"type":504,"tag":661,"props":1456,"children":1457},{"__ignoreMap":658},[1458,1466,1490,1497,1525,1574,1581,1600,1630,1638,1646,1689,1718,1726,1733,1761,1769,1785,1792,1799,1818,1833,1840,1847,1854,1866],{"type":504,"tag":665,"props":1459,"children":1460},{"class":667,"line":668},[1461],{"type":504,"tag":665,"props":1462,"children":1463},{"style":672},[1464],{"type":510,"value":1465},"\u002F\u002F useEventSource.js\n",{"type":504,"tag":665,"props":1467,"children":1468},{"class":667,"line":678},[1469,1473,1478,1482,1486],{"type":504,"tag":665,"props":1470,"children":1471},{"style":682},[1472],{"type":510,"value":685},{"type":504,"tag":665,"props":1474,"children":1475},{"style":688},[1476],{"type":510,"value":1477}," { useState, useEffect } ",{"type":504,"tag":665,"props":1479,"children":1480},{"style":682},[1481],{"type":510,"value":696},{"type":504,"tag":665,"props":1483,"children":1484},{"style":699},[1485],{"type":510,"value":702},{"type":504,"tag":665,"props":1487,"children":1488},{"style":688},[1489],{"type":510,"value":707},{"type":504,"tag":665,"props":1491,"children":1492},{"class":667,"line":710},[1493],{"type":504,"tag":665,"props":1494,"children":1495},{"emptyLinePlaceholder":714},[1496],{"type":510,"value":717},{"type":504,"tag":665,"props":1498,"children":1499},{"class":667,"line":720},[1500,1504,1508,1513,1517,1521],{"type":504,"tag":665,"props":1501,"children":1502},{"style":682},[1503],{"type":510,"value":726},{"type":504,"tag":665,"props":1505,"children":1506},{"style":682},[1507],{"type":510,"value":731},{"type":504,"tag":665,"props":1509,"children":1510},{"style":734},[1511],{"type":510,"value":1512}," useEventSource",{"type":504,"tag":665,"props":1514,"children":1515},{"style":688},[1516],{"type":510,"value":742},{"type":504,"tag":665,"props":1518,"children":1519},{"style":745},[1520],{"type":510,"value":748},{"type":504,"tag":665,"props":1522,"children":1523},{"style":688},[1524],{"type":510,"value":753},{"type":504,"tag":665,"props":1526,"children":1527},{"class":667,"line":756},[1528,1532,1536,1541,1545,1550,1554,1558,1562,1566,1570],{"type":504,"tag":665,"props":1529,"children":1530},{"style":682},[1531],{"type":510,"value":762},{"type":504,"tag":665,"props":1533,"children":1534},{"style":688},[1535],{"type":510,"value":767},{"type":504,"tag":665,"props":1537,"children":1538},{"style":770},[1539],{"type":510,"value":1540},"data",{"type":504,"tag":665,"props":1542,"children":1543},{"style":688},[1544],{"type":510,"value":778},{"type":504,"tag":665,"props":1546,"children":1547},{"style":770},[1548],{"type":510,"value":1549},"setData",{"type":504,"tag":665,"props":1551,"children":1552},{"style":688},[1553],{"type":510,"value":788},{"type":504,"tag":665,"props":1555,"children":1556},{"style":682},[1557],{"type":510,"value":793},{"type":504,"tag":665,"props":1559,"children":1560},{"style":734},[1561],{"type":510,"value":798},{"type":504,"tag":665,"props":1563,"children":1564},{"style":688},[1565],{"type":510,"value":742},{"type":504,"tag":665,"props":1567,"children":1568},{"style":770},[1569],{"type":510,"value":835},{"type":504,"tag":665,"props":1571,"children":1572},{"style":688},[1573],{"type":510,"value":840},{"type":504,"tag":665,"props":1575,"children":1576},{"class":667,"line":806},[1577],{"type":504,"tag":665,"props":1578,"children":1579},{"emptyLinePlaceholder":714},[1580],{"type":510,"value":717},{"type":504,"tag":665,"props":1582,"children":1583},{"class":667,"line":843},[1584,1588,1592,1596],{"type":504,"tag":665,"props":1585,"children":1586},{"style":734},[1587],{"type":510,"value":857},{"type":504,"tag":665,"props":1589,"children":1590},{"style":688},[1591],{"type":510,"value":862},{"type":504,"tag":665,"props":1593,"children":1594},{"style":682},[1595],{"type":510,"value":867},{"type":504,"tag":665,"props":1597,"children":1598},{"style":688},[1599],{"type":510,"value":872},{"type":504,"tag":665,"props":1601,"children":1602},{"class":667,"line":851},[1603,1608,1613,1617,1621,1626],{"type":504,"tag":665,"props":1604,"children":1605},{"style":682},[1606],{"type":510,"value":1607},"    const",{"type":504,"tag":665,"props":1609,"children":1610},{"style":770},[1611],{"type":510,"value":1612}," eventSource",{"type":504,"tag":665,"props":1614,"children":1615},{"style":682},[1616],{"type":510,"value":821},{"type":504,"tag":665,"props":1618,"children":1619},{"style":682},[1620],{"type":510,"value":890},{"type":504,"tag":665,"props":1622,"children":1623},{"style":734},[1624],{"type":510,"value":1625}," EventSource",{"type":504,"tag":665,"props":1627,"children":1628},{"style":688},[1629],{"type":510,"value":900},{"type":504,"tag":665,"props":1631,"children":1632},{"class":667,"line":875},[1633],{"type":504,"tag":665,"props":1634,"children":1635},{"style":688},[1636],{"type":510,"value":1637},"    \n",{"type":504,"tag":665,"props":1639,"children":1640},{"class":667,"line":903},[1641],{"type":504,"tag":665,"props":1642,"children":1643},{"style":672},[1644],{"type":510,"value":1645},"    \u002F\u002F 监听特定类型的事件\n",{"type":504,"tag":665,"props":1647,"children":1648},{"class":667,"line":953},[1649,1654,1659,1663,1668,1673,1677,1681,1685],{"type":504,"tag":665,"props":1650,"children":1651},{"style":688},[1652],{"type":510,"value":1653},"    eventSource.",{"type":504,"tag":665,"props":1655,"children":1656},{"style":734},[1657],{"type":510,"value":1658},"addEventListener",{"type":504,"tag":665,"props":1660,"children":1661},{"style":688},[1662],{"type":510,"value":742},{"type":504,"tag":665,"props":1664,"children":1665},{"style":699},[1666],{"type":510,"value":1667},"'update'",{"type":504,"tag":665,"props":1669,"children":1670},{"style":688},[1671],{"type":510,"value":1672},", (",{"type":504,"tag":665,"props":1674,"children":1675},{"style":745},[1676],{"type":510,"value":1031},{"type":504,"tag":665,"props":1678,"children":1679},{"style":688},[1680],{"type":510,"value":1036},{"type":504,"tag":665,"props":1682,"children":1683},{"style":682},[1684],{"type":510,"value":867},{"type":504,"tag":665,"props":1686,"children":1687},{"style":688},[1688],{"type":510,"value":872},{"type":504,"tag":665,"props":1690,"children":1691},{"class":667,"line":999},[1692,1697,1701,1705,1709,1713],{"type":504,"tag":665,"props":1693,"children":1694},{"style":734},[1695],{"type":510,"value":1696},"      setData",{"type":504,"tag":665,"props":1698,"children":1699},{"style":688},[1700],{"type":510,"value":742},{"type":504,"tag":665,"props":1702,"children":1703},{"style":770},[1704],{"type":510,"value":1086},{"type":504,"tag":665,"props":1706,"children":1707},{"style":688},[1708],{"type":510,"value":1091},{"type":504,"tag":665,"props":1710,"children":1711},{"style":734},[1712],{"type":510,"value":1096},{"type":504,"tag":665,"props":1714,"children":1715},{"style":688},[1716],{"type":510,"value":1717},"(event.data));\n",{"type":504,"tag":665,"props":1719,"children":1720},{"class":667,"line":1007},[1721],{"type":504,"tag":665,"props":1722,"children":1723},{"style":688},[1724],{"type":510,"value":1725},"    });\n",{"type":504,"tag":665,"props":1727,"children":1728},{"class":667,"line":1047},[1729],{"type":504,"tag":665,"props":1730,"children":1731},{"emptyLinePlaceholder":714},[1732],{"type":510,"value":717},{"type":504,"tag":665,"props":1734,"children":1735},{"class":667,"line":1104},[1736,1740,1745,1749,1753,1757],{"type":504,"tag":665,"props":1737,"children":1738},{"style":688},[1739],{"type":510,"value":1653},{"type":504,"tag":665,"props":1741,"children":1742},{"style":734},[1743],{"type":510,"value":1744},"onerror",{"type":504,"tag":665,"props":1746,"children":1747},{"style":682},[1748],{"type":510,"value":821},{"type":504,"tag":665,"props":1750,"children":1751},{"style":688},[1752],{"type":510,"value":923},{"type":504,"tag":665,"props":1754,"children":1755},{"style":682},[1756],{"type":510,"value":867},{"type":504,"tag":665,"props":1758,"children":1759},{"style":688},[1760],{"type":510,"value":872},{"type":504,"tag":665,"props":1762,"children":1763},{"class":667,"line":1113},[1764],{"type":504,"tag":665,"props":1765,"children":1766},{"style":672},[1767],{"type":510,"value":1768},"      \u002F\u002F 处理错误，可以关闭连接\n",{"type":504,"tag":665,"props":1770,"children":1771},{"class":667,"line":1121},[1772,1777,1781],{"type":504,"tag":665,"props":1773,"children":1774},{"style":688},[1775],{"type":510,"value":1776},"      eventSource.",{"type":504,"tag":665,"props":1778,"children":1779},{"style":734},[1780],{"type":510,"value":1153},{"type":504,"tag":665,"props":1782,"children":1783},{"style":688},[1784],{"type":510,"value":1158},{"type":504,"tag":665,"props":1786,"children":1787},{"class":667,"line":1142},[1788],{"type":504,"tag":665,"props":1789,"children":1790},{"style":688},[1791],{"type":510,"value":1110},{"type":504,"tag":665,"props":1793,"children":1794},{"class":667,"line":1161},[1795],{"type":504,"tag":665,"props":1796,"children":1797},{"emptyLinePlaceholder":714},[1798],{"type":510,"value":717},{"type":504,"tag":665,"props":1800,"children":1801},{"class":667,"line":1169},[1802,1806,1810,1814],{"type":504,"tag":665,"props":1803,"children":1804},{"style":682},[1805],{"type":510,"value":1127},{"type":504,"tag":665,"props":1807,"children":1808},{"style":688},[1809],{"type":510,"value":923},{"type":504,"tag":665,"props":1811,"children":1812},{"style":682},[1813],{"type":510,"value":867},{"type":504,"tag":665,"props":1815,"children":1816},{"style":688},[1817],{"type":510,"value":872},{"type":504,"tag":665,"props":1819,"children":1820},{"class":667,"line":1178},[1821,1825,1829],{"type":504,"tag":665,"props":1822,"children":1823},{"style":688},[1824],{"type":510,"value":1776},{"type":504,"tag":665,"props":1826,"children":1827},{"style":734},[1828],{"type":510,"value":1153},{"type":504,"tag":665,"props":1830,"children":1831},{"style":688},[1832],{"type":510,"value":1158},{"type":504,"tag":665,"props":1834,"children":1835},{"class":667,"line":1186},[1836],{"type":504,"tag":665,"props":1837,"children":1838},{"style":688},[1839],{"type":510,"value":1110},{"type":504,"tag":665,"props":1841,"children":1842},{"class":667,"line":1224},[1843],{"type":504,"tag":665,"props":1844,"children":1845},{"style":688},[1846],{"type":510,"value":1175},{"type":504,"tag":665,"props":1848,"children":1849},{"class":667,"line":5},[1850],{"type":504,"tag":665,"props":1851,"children":1852},{"emptyLinePlaceholder":714},[1853],{"type":510,"value":717},{"type":504,"tag":665,"props":1855,"children":1856},{"class":667,"line":1291},[1857,1861],{"type":504,"tag":665,"props":1858,"children":1859},{"style":682},[1860],{"type":510,"value":1323},{"type":504,"tag":665,"props":1862,"children":1863},{"style":688},[1864],{"type":510,"value":1865}," data;\n",{"type":504,"tag":665,"props":1867,"children":1868},{"class":667,"line":1300},[1869],{"type":504,"tag":665,"props":1870,"children":1871},{"style":688},[1872],{"type":510,"value":1337},{"type":504,"tag":528,"props":1874,"children":1875},{},[],{"type":504,"tag":532,"props":1877,"children":1879},{"id":1878},"_2-离线能力pwa-与-service-worker",[1880],{"type":510,"value":1881},"2. 离线能力：PWA 与 Service Worker",{"type":504,"tag":544,"props":1883,"children":1885},{"id":1884},"a-pwa-progressive-web-app",[1886],{"type":510,"value":1887},"A. PWA (Progressive Web App)",{"type":504,"tag":516,"props":1889,"children":1890},{},[1891],{"type":510,"value":1892},"PWA 不是一种单一的技术，而是一套使 Web 应用具备类似原生应用（如可安装、离线工作、推送通知）体验的最佳实践和 Web 标准的集合。",{"type":504,"tag":516,"props":1894,"children":1895},{},[1896,1901],{"type":504,"tag":520,"props":1897,"children":1898},{},[1899],{"type":510,"value":1900},"核心要求",{"type":510,"value":1902},"：",{"type":504,"tag":1904,"props":1905,"children":1906},"ol",{},[1907,1917,1927],{"type":504,"tag":567,"props":1908,"children":1909},{},[1910,1915],{"type":504,"tag":520,"props":1911,"children":1912},{},[1913],{"type":510,"value":1914},"必须通过 HTTPS 提供服务",{"type":510,"value":1916},"。",{"type":504,"tag":567,"props":1918,"children":1919},{},[1920,1925],{"type":504,"tag":520,"props":1921,"children":1922},{},[1923],{"type":510,"value":1924},"必须有一个 Web App Manifest (清单文件)",{"type":510,"value":1926},"：一个 JSON 文件，定义了应用的名称、图标、启动 URL、显示模式等，使其“可安装”。",{"type":504,"tag":567,"props":1928,"children":1929},{},[1930,1935],{"type":504,"tag":520,"props":1931,"children":1932},{},[1933],{"type":510,"value":1934},"必须注册一个 Service Worker",{"type":510,"value":1936},"：这是实现离线能力和推送通知的关键。",{"type":504,"tag":544,"props":1938,"children":1940},{"id":1939},"b-service-worker",[1941],{"type":510,"value":1942},"B. Service Worker",{"type":504,"tag":516,"props":1944,"children":1945},{},[1946,1948,1953],{"type":510,"value":1947},"Service Worker 是一个在浏览器后台独立于主线程运行的 JavaScript 脚本。它充当了 Web 应用、浏览器和网络之间的",{"type":504,"tag":520,"props":1949,"children":1950},{},[1951],{"type":510,"value":1952},"网络代理",{"type":510,"value":1916},{"type":504,"tag":563,"props":1955,"children":1956},{},[1957,1973,2011],{"type":504,"tag":567,"props":1958,"children":1959},{},[1960,1965,1966,1971],{"type":504,"tag":520,"props":1961,"children":1962},{},[1963],{"type":510,"value":1964},"核心能力",{"type":510,"value":1902},{"type":504,"tag":520,"props":1967,"children":1968},{},[1969],{"type":510,"value":1970},"拦截和处理网络请求",{"type":510,"value":1972},"。它可以检查请求，并决定是从网络获取响应，还是从缓存中提供响应。",{"type":504,"tag":567,"props":1974,"children":1975},{},[1976,1981,1982,1988,1990,1996,1997,2003,2005,2010],{"type":504,"tag":520,"props":1977,"children":1978},{},[1979],{"type":510,"value":1980},"生命周期",{"type":510,"value":1902},{"type":504,"tag":661,"props":1983,"children":1985},{"className":1984},[],[1986],{"type":510,"value":1987},"install",{"type":510,"value":1989}," -> ",{"type":504,"tag":661,"props":1991,"children":1993},{"className":1992},[],[1994],{"type":510,"value":1995},"activate",{"type":510,"value":1989},{"type":504,"tag":661,"props":1998,"children":2000},{"className":1999},[],[2001],{"type":510,"value":2002},"fetch",{"type":510,"value":2004}," \u002F ",{"type":504,"tag":661,"props":2006,"children":2008},{"className":2007},[],[2009],{"type":510,"value":1209},{"type":510,"value":1916},{"type":504,"tag":567,"props":2012,"children":2013},{},[2014,2019,2020],{"type":504,"tag":520,"props":2015,"children":2016},{},[2017],{"type":510,"value":2018},"离线缓存策略",{"type":510,"value":576},{"type":504,"tag":563,"props":2021,"children":2022},{},[2023,2033,2043],{"type":504,"tag":567,"props":2024,"children":2025},{},[2026,2031],{"type":504,"tag":520,"props":2027,"children":2028},{},[2029],{"type":510,"value":2030},"Cache First (缓存优先)",{"type":510,"value":2032},"：优先从缓存中读取。如果缓存中没有，再发起网络请求，并将成功的结果存入缓存。适合静态资源（JS, CSS, 图片）。",{"type":504,"tag":567,"props":2034,"children":2035},{},[2036,2041],{"type":504,"tag":520,"props":2037,"children":2038},{},[2039],{"type":510,"value":2040},"Network First (网络优先)",{"type":510,"value":2042},"：优先从网络获取。如果网络失败，再从缓存中读取。适合需要最新数据的 API 请求（如新闻列表）。",{"type":504,"tag":567,"props":2044,"children":2045},{},[2046,2051],{"type":504,"tag":520,"props":2047,"children":2048},{},[2049],{"type":510,"value":2050},"Stale-While-Revalidate (后台更新)",{"type":510,"value":2052},"：同时从缓存和网络获取。立即返回缓存的数据（让用户快速看到内容），然后用网络请求回来的新数据更新缓存，并在下次访问时使用。这是平衡性能和数据新鲜度的绝佳策略。",{"type":504,"tag":516,"props":2054,"children":2055},{},[2056],{"type":504,"tag":520,"props":2057,"children":2058},{},[2059,2061,2067],{"type":510,"value":2060},"示例：一个简单的 Service Worker (",{"type":504,"tag":661,"props":2062,"children":2064},{"className":2063},[],[2065],{"type":510,"value":2066},"sw.js",{"type":510,"value":2068},")",{"type":504,"tag":653,"props":2070,"children":2074},{"className":2071,"code":2072,"language":2073,"meta":658,"style":658},"language-javascript shiki shiki-themes github-light github-dark","const CACHE_NAME = 'my-app-cache-v1';\nconst URLS_TO_CACHE = [\n  '\u002F',\n  '\u002Fstyles\u002Fmain.css',\n  '\u002Fscript\u002Fmain.js',\n  '\u002Fimages\u002Flogo.png'\n];\n\n\u002F\u002F 安装时，缓存核心静态资源\nself.addEventListener('install', event => {\n  event.waitUntil(\n    caches.open(CACHE_NAME)\n      .then(cache => {\n        return cache.addAll(URLS_TO_CACHE);\n      })\n  );\n});\n\n\u002F\u002F 拦截 fetch 事件，实施 Cache First 策略\nself.addEventListener('fetch', event => {\n  event.respondWith(\n    caches.match(event.request)\n      .then(response => {\n        \u002F\u002F 如果缓存中有匹配的响应，则返回它\n        if (response) {\n          return response;\n        }\n        \u002F\u002F 否则，通过网络获取\n        return fetch(event.request);\n      })\n  );\n});\n","javascript",[2075],{"type":504,"tag":661,"props":2076,"children":2077},{"__ignoreMap":658},[2078,2104,2125,2138,2150,2162,2170,2178,2185,2193,2230,2248,2275,2305,2336,2344,2352,2360,2367,2375,2411,2427,2444,2472,2480,2493,2506,2514,2522,2539,2547,2555],{"type":504,"tag":665,"props":2079,"children":2080},{"class":667,"line":668},[2081,2086,2091,2095,2100],{"type":504,"tag":665,"props":2082,"children":2083},{"style":682},[2084],{"type":510,"value":2085},"const",{"type":504,"tag":665,"props":2087,"children":2088},{"style":770},[2089],{"type":510,"value":2090}," CACHE_NAME",{"type":504,"tag":665,"props":2092,"children":2093},{"style":682},[2094],{"type":510,"value":821},{"type":504,"tag":665,"props":2096,"children":2097},{"style":699},[2098],{"type":510,"value":2099}," 'my-app-cache-v1'",{"type":504,"tag":665,"props":2101,"children":2102},{"style":688},[2103],{"type":510,"value":707},{"type":504,"tag":665,"props":2105,"children":2106},{"class":667,"line":678},[2107,2111,2116,2120],{"type":504,"tag":665,"props":2108,"children":2109},{"style":682},[2110],{"type":510,"value":2085},{"type":504,"tag":665,"props":2112,"children":2113},{"style":770},[2114],{"type":510,"value":2115}," URLS_TO_CACHE",{"type":504,"tag":665,"props":2117,"children":2118},{"style":682},[2119],{"type":510,"value":821},{"type":504,"tag":665,"props":2121,"children":2122},{"style":688},[2123],{"type":510,"value":2124}," [\n",{"type":504,"tag":665,"props":2126,"children":2127},{"class":667,"line":710},[2128,2133],{"type":504,"tag":665,"props":2129,"children":2130},{"style":699},[2131],{"type":510,"value":2132},"  '\u002F'",{"type":504,"tag":665,"props":2134,"children":2135},{"style":688},[2136],{"type":510,"value":2137},",\n",{"type":504,"tag":665,"props":2139,"children":2140},{"class":667,"line":720},[2141,2146],{"type":504,"tag":665,"props":2142,"children":2143},{"style":699},[2144],{"type":510,"value":2145},"  '\u002Fstyles\u002Fmain.css'",{"type":504,"tag":665,"props":2147,"children":2148},{"style":688},[2149],{"type":510,"value":2137},{"type":504,"tag":665,"props":2151,"children":2152},{"class":667,"line":756},[2153,2158],{"type":504,"tag":665,"props":2154,"children":2155},{"style":699},[2156],{"type":510,"value":2157},"  '\u002Fscript\u002Fmain.js'",{"type":504,"tag":665,"props":2159,"children":2160},{"style":688},[2161],{"type":510,"value":2137},{"type":504,"tag":665,"props":2163,"children":2164},{"class":667,"line":806},[2165],{"type":504,"tag":665,"props":2166,"children":2167},{"style":699},[2168],{"type":510,"value":2169},"  '\u002Fimages\u002Flogo.png'\n",{"type":504,"tag":665,"props":2171,"children":2172},{"class":667,"line":843},[2173],{"type":504,"tag":665,"props":2174,"children":2175},{"style":688},[2176],{"type":510,"value":2177},"];\n",{"type":504,"tag":665,"props":2179,"children":2180},{"class":667,"line":851},[2181],{"type":504,"tag":665,"props":2182,"children":2183},{"emptyLinePlaceholder":714},[2184],{"type":510,"value":717},{"type":504,"tag":665,"props":2186,"children":2187},{"class":667,"line":875},[2188],{"type":504,"tag":665,"props":2189,"children":2190},{"style":672},[2191],{"type":510,"value":2192},"\u002F\u002F 安装时，缓存核心静态资源\n",{"type":504,"tag":665,"props":2194,"children":2195},{"class":667,"line":903},[2196,2201,2205,2209,2214,2218,2222,2226],{"type":504,"tag":665,"props":2197,"children":2198},{"style":688},[2199],{"type":510,"value":2200},"self.",{"type":504,"tag":665,"props":2202,"children":2203},{"style":734},[2204],{"type":510,"value":1658},{"type":504,"tag":665,"props":2206,"children":2207},{"style":688},[2208],{"type":510,"value":742},{"type":504,"tag":665,"props":2210,"children":2211},{"style":699},[2212],{"type":510,"value":2213},"'install'",{"type":504,"tag":665,"props":2215,"children":2216},{"style":688},[2217],{"type":510,"value":778},{"type":504,"tag":665,"props":2219,"children":2220},{"style":745},[2221],{"type":510,"value":1031},{"type":504,"tag":665,"props":2223,"children":2224},{"style":682},[2225],{"type":510,"value":1067},{"type":504,"tag":665,"props":2227,"children":2228},{"style":688},[2229],{"type":510,"value":872},{"type":504,"tag":665,"props":2231,"children":2232},{"class":667,"line":953},[2233,2238,2243],{"type":504,"tag":665,"props":2234,"children":2235},{"style":688},[2236],{"type":510,"value":2237},"  event.",{"type":504,"tag":665,"props":2239,"children":2240},{"style":734},[2241],{"type":510,"value":2242},"waitUntil",{"type":504,"tag":665,"props":2244,"children":2245},{"style":688},[2246],{"type":510,"value":2247},"(\n",{"type":504,"tag":665,"props":2249,"children":2250},{"class":667,"line":999},[2251,2256,2261,2265,2270],{"type":504,"tag":665,"props":2252,"children":2253},{"style":688},[2254],{"type":510,"value":2255},"    caches.",{"type":504,"tag":665,"props":2257,"children":2258},{"style":734},[2259],{"type":510,"value":2260},"open",{"type":504,"tag":665,"props":2262,"children":2263},{"style":688},[2264],{"type":510,"value":742},{"type":504,"tag":665,"props":2266,"children":2267},{"style":770},[2268],{"type":510,"value":2269},"CACHE_NAME",{"type":504,"tag":665,"props":2271,"children":2272},{"style":688},[2273],{"type":510,"value":2274},")\n",{"type":504,"tag":665,"props":2276,"children":2277},{"class":667,"line":1007},[2278,2283,2288,2292,2297,2301],{"type":504,"tag":665,"props":2279,"children":2280},{"style":688},[2281],{"type":510,"value":2282},"      .",{"type":504,"tag":665,"props":2284,"children":2285},{"style":734},[2286],{"type":510,"value":2287},"then",{"type":504,"tag":665,"props":2289,"children":2290},{"style":688},[2291],{"type":510,"value":742},{"type":504,"tag":665,"props":2293,"children":2294},{"style":745},[2295],{"type":510,"value":2296},"cache",{"type":504,"tag":665,"props":2298,"children":2299},{"style":682},[2300],{"type":510,"value":1067},{"type":504,"tag":665,"props":2302,"children":2303},{"style":688},[2304],{"type":510,"value":872},{"type":504,"tag":665,"props":2306,"children":2307},{"class":667,"line":1047},[2308,2313,2318,2323,2327,2332],{"type":504,"tag":665,"props":2309,"children":2310},{"style":682},[2311],{"type":510,"value":2312},"        return",{"type":504,"tag":665,"props":2314,"children":2315},{"style":688},[2316],{"type":510,"value":2317}," cache.",{"type":504,"tag":665,"props":2319,"children":2320},{"style":734},[2321],{"type":510,"value":2322},"addAll",{"type":504,"tag":665,"props":2324,"children":2325},{"style":688},[2326],{"type":510,"value":742},{"type":504,"tag":665,"props":2328,"children":2329},{"style":770},[2330],{"type":510,"value":2331},"URLS_TO_CACHE",{"type":504,"tag":665,"props":2333,"children":2334},{"style":688},[2335],{"type":510,"value":840},{"type":504,"tag":665,"props":2337,"children":2338},{"class":667,"line":1104},[2339],{"type":504,"tag":665,"props":2340,"children":2341},{"style":688},[2342],{"type":510,"value":2343},"      })\n",{"type":504,"tag":665,"props":2345,"children":2346},{"class":667,"line":1113},[2347],{"type":504,"tag":665,"props":2348,"children":2349},{"style":688},[2350],{"type":510,"value":2351},"  );\n",{"type":504,"tag":665,"props":2353,"children":2354},{"class":667,"line":1121},[2355],{"type":504,"tag":665,"props":2356,"children":2357},{"style":688},[2358],{"type":510,"value":2359},"});\n",{"type":504,"tag":665,"props":2361,"children":2362},{"class":667,"line":1142},[2363],{"type":504,"tag":665,"props":2364,"children":2365},{"emptyLinePlaceholder":714},[2366],{"type":510,"value":717},{"type":504,"tag":665,"props":2368,"children":2369},{"class":667,"line":1161},[2370],{"type":504,"tag":665,"props":2371,"children":2372},{"style":672},[2373],{"type":510,"value":2374},"\u002F\u002F 拦截 fetch 事件，实施 Cache First 策略\n",{"type":504,"tag":665,"props":2376,"children":2377},{"class":667,"line":1169},[2378,2382,2386,2390,2395,2399,2403,2407],{"type":504,"tag":665,"props":2379,"children":2380},{"style":688},[2381],{"type":510,"value":2200},{"type":504,"tag":665,"props":2383,"children":2384},{"style":734},[2385],{"type":510,"value":1658},{"type":504,"tag":665,"props":2387,"children":2388},{"style":688},[2389],{"type":510,"value":742},{"type":504,"tag":665,"props":2391,"children":2392},{"style":699},[2393],{"type":510,"value":2394},"'fetch'",{"type":504,"tag":665,"props":2396,"children":2397},{"style":688},[2398],{"type":510,"value":778},{"type":504,"tag":665,"props":2400,"children":2401},{"style":745},[2402],{"type":510,"value":1031},{"type":504,"tag":665,"props":2404,"children":2405},{"style":682},[2406],{"type":510,"value":1067},{"type":504,"tag":665,"props":2408,"children":2409},{"style":688},[2410],{"type":510,"value":872},{"type":504,"tag":665,"props":2412,"children":2413},{"class":667,"line":1178},[2414,2418,2423],{"type":504,"tag":665,"props":2415,"children":2416},{"style":688},[2417],{"type":510,"value":2237},{"type":504,"tag":665,"props":2419,"children":2420},{"style":734},[2421],{"type":510,"value":2422},"respondWith",{"type":504,"tag":665,"props":2424,"children":2425},{"style":688},[2426],{"type":510,"value":2247},{"type":504,"tag":665,"props":2428,"children":2429},{"class":667,"line":1186},[2430,2434,2439],{"type":504,"tag":665,"props":2431,"children":2432},{"style":688},[2433],{"type":510,"value":2255},{"type":504,"tag":665,"props":2435,"children":2436},{"style":734},[2437],{"type":510,"value":2438},"match",{"type":504,"tag":665,"props":2440,"children":2441},{"style":688},[2442],{"type":510,"value":2443},"(event.request)\n",{"type":504,"tag":665,"props":2445,"children":2446},{"class":667,"line":1224},[2447,2451,2455,2459,2464,2468],{"type":504,"tag":665,"props":2448,"children":2449},{"style":688},[2450],{"type":510,"value":2282},{"type":504,"tag":665,"props":2452,"children":2453},{"style":734},[2454],{"type":510,"value":2287},{"type":504,"tag":665,"props":2456,"children":2457},{"style":688},[2458],{"type":510,"value":742},{"type":504,"tag":665,"props":2460,"children":2461},{"style":745},[2462],{"type":510,"value":2463},"response",{"type":504,"tag":665,"props":2465,"children":2466},{"style":682},[2467],{"type":510,"value":1067},{"type":504,"tag":665,"props":2469,"children":2470},{"style":688},[2471],{"type":510,"value":872},{"type":504,"tag":665,"props":2473,"children":2474},{"class":667,"line":5},[2475],{"type":504,"tag":665,"props":2476,"children":2477},{"style":672},[2478],{"type":510,"value":2479},"        \u002F\u002F 如果缓存中有匹配的响应，则返回它\n",{"type":504,"tag":665,"props":2481,"children":2482},{"class":667,"line":1291},[2483,2488],{"type":504,"tag":665,"props":2484,"children":2485},{"style":682},[2486],{"type":510,"value":2487},"        if",{"type":504,"tag":665,"props":2489,"children":2490},{"style":688},[2491],{"type":510,"value":2492}," (response) {\n",{"type":504,"tag":665,"props":2494,"children":2495},{"class":667,"line":1300},[2496,2501],{"type":504,"tag":665,"props":2497,"children":2498},{"style":682},[2499],{"type":510,"value":2500},"          return",{"type":504,"tag":665,"props":2502,"children":2503},{"style":688},[2504],{"type":510,"value":2505}," response;\n",{"type":504,"tag":665,"props":2507,"children":2508},{"class":667,"line":1309},[2509],{"type":504,"tag":665,"props":2510,"children":2511},{"style":688},[2512],{"type":510,"value":2513},"        }\n",{"type":504,"tag":665,"props":2515,"children":2516},{"class":667,"line":1317},[2517],{"type":504,"tag":665,"props":2518,"children":2519},{"style":672},[2520],{"type":510,"value":2521},"        \u002F\u002F 否则，通过网络获取\n",{"type":504,"tag":665,"props":2523,"children":2524},{"class":667,"line":1331},[2525,2529,2534],{"type":504,"tag":665,"props":2526,"children":2527},{"style":682},[2528],{"type":510,"value":2312},{"type":504,"tag":665,"props":2530,"children":2531},{"style":734},[2532],{"type":510,"value":2533}," fetch",{"type":504,"tag":665,"props":2535,"children":2536},{"style":688},[2537],{"type":510,"value":2538},"(event.request);\n",{"type":504,"tag":665,"props":2540,"children":2542},{"class":667,"line":2541},30,[2543],{"type":504,"tag":665,"props":2544,"children":2545},{"style":688},[2546],{"type":510,"value":2343},{"type":504,"tag":665,"props":2548,"children":2550},{"class":667,"line":2549},31,[2551],{"type":504,"tag":665,"props":2552,"children":2553},{"style":688},[2554],{"type":510,"value":2351},{"type":504,"tag":665,"props":2556,"children":2558},{"class":667,"line":2557},32,[2559],{"type":504,"tag":665,"props":2560,"children":2561},{"style":688},[2562],{"type":510,"value":2359},{"type":504,"tag":516,"props":2564,"children":2565},{},[2566,2571],{"type":504,"tag":520,"props":2567,"children":2568},{},[2569],{"type":510,"value":2570},"在应用中注册 Service Worker",{"type":510,"value":1902},{"type":504,"tag":653,"props":2573,"children":2575},{"className":2071,"code":2574,"language":2073,"meta":658,"style":658},"if ('serviceWorker' in navigator) {\n  window.addEventListener('load', () => {\n    navigator.serviceWorker.register('\u002Fsw.js')\n      .then(registration => console.log('ServiceWorker registration successful'))\n      .catch(err => console.log('ServiceWorker registration failed: ', err));\n  });\n}\n",[2576],{"type":504,"tag":661,"props":2577,"children":2578},{"__ignoreMap":658},[2579,2606,2640,2666,2712,2759,2767],{"type":504,"tag":665,"props":2580,"children":2581},{"class":667,"line":668},[2582,2587,2591,2596,2601],{"type":504,"tag":665,"props":2583,"children":2584},{"style":682},[2585],{"type":510,"value":2586},"if",{"type":504,"tag":665,"props":2588,"children":2589},{"style":688},[2590],{"type":510,"value":1026},{"type":504,"tag":665,"props":2592,"children":2593},{"style":699},[2594],{"type":510,"value":2595},"'serviceWorker'",{"type":504,"tag":665,"props":2597,"children":2598},{"style":682},[2599],{"type":510,"value":2600}," in",{"type":504,"tag":665,"props":2602,"children":2603},{"style":688},[2604],{"type":510,"value":2605}," navigator) {\n",{"type":504,"tag":665,"props":2607,"children":2608},{"class":667,"line":678},[2609,2614,2618,2622,2627,2632,2636],{"type":504,"tag":665,"props":2610,"children":2611},{"style":688},[2612],{"type":510,"value":2613},"  window.",{"type":504,"tag":665,"props":2615,"children":2616},{"style":734},[2617],{"type":510,"value":1658},{"type":504,"tag":665,"props":2619,"children":2620},{"style":688},[2621],{"type":510,"value":742},{"type":504,"tag":665,"props":2623,"children":2624},{"style":699},[2625],{"type":510,"value":2626},"'load'",{"type":504,"tag":665,"props":2628,"children":2629},{"style":688},[2630],{"type":510,"value":2631},", () ",{"type":504,"tag":665,"props":2633,"children":2634},{"style":682},[2635],{"type":510,"value":867},{"type":504,"tag":665,"props":2637,"children":2638},{"style":688},[2639],{"type":510,"value":872},{"type":504,"tag":665,"props":2641,"children":2642},{"class":667,"line":710},[2643,2648,2653,2657,2662],{"type":504,"tag":665,"props":2644,"children":2645},{"style":688},[2646],{"type":510,"value":2647},"    navigator.serviceWorker.",{"type":504,"tag":665,"props":2649,"children":2650},{"style":734},[2651],{"type":510,"value":2652},"register",{"type":504,"tag":665,"props":2654,"children":2655},{"style":688},[2656],{"type":510,"value":742},{"type":504,"tag":665,"props":2658,"children":2659},{"style":699},[2660],{"type":510,"value":2661},"'\u002Fsw.js'",{"type":504,"tag":665,"props":2663,"children":2664},{"style":688},[2665],{"type":510,"value":2274},{"type":504,"tag":665,"props":2667,"children":2668},{"class":667,"line":720},[2669,2673,2677,2681,2686,2690,2694,2698,2702,2707],{"type":504,"tag":665,"props":2670,"children":2671},{"style":688},[2672],{"type":510,"value":2282},{"type":504,"tag":665,"props":2674,"children":2675},{"style":734},[2676],{"type":510,"value":2287},{"type":504,"tag":665,"props":2678,"children":2679},{"style":688},[2680],{"type":510,"value":742},{"type":504,"tag":665,"props":2682,"children":2683},{"style":745},[2684],{"type":510,"value":2685},"registration",{"type":504,"tag":665,"props":2687,"children":2688},{"style":682},[2689],{"type":510,"value":1067},{"type":504,"tag":665,"props":2691,"children":2692},{"style":688},[2693],{"type":510,"value":932},{"type":504,"tag":665,"props":2695,"children":2696},{"style":734},[2697],{"type":510,"value":937},{"type":504,"tag":665,"props":2699,"children":2700},{"style":688},[2701],{"type":510,"value":742},{"type":504,"tag":665,"props":2703,"children":2704},{"style":699},[2705],{"type":510,"value":2706},"'ServiceWorker registration successful'",{"type":504,"tag":665,"props":2708,"children":2709},{"style":688},[2710],{"type":510,"value":2711},"))\n",{"type":504,"tag":665,"props":2713,"children":2714},{"class":667,"line":756},[2715,2719,2724,2728,2733,2737,2741,2745,2749,2754],{"type":504,"tag":665,"props":2716,"children":2717},{"style":688},[2718],{"type":510,"value":2282},{"type":504,"tag":665,"props":2720,"children":2721},{"style":734},[2722],{"type":510,"value":2723},"catch",{"type":504,"tag":665,"props":2725,"children":2726},{"style":688},[2727],{"type":510,"value":742},{"type":504,"tag":665,"props":2729,"children":2730},{"style":745},[2731],{"type":510,"value":2732},"err",{"type":504,"tag":665,"props":2734,"children":2735},{"style":682},[2736],{"type":510,"value":1067},{"type":504,"tag":665,"props":2738,"children":2739},{"style":688},[2740],{"type":510,"value":932},{"type":504,"tag":665,"props":2742,"children":2743},{"style":734},[2744],{"type":510,"value":937},{"type":504,"tag":665,"props":2746,"children":2747},{"style":688},[2748],{"type":510,"value":742},{"type":504,"tag":665,"props":2750,"children":2751},{"style":699},[2752],{"type":510,"value":2753},"'ServiceWorker registration failed: '",{"type":504,"tag":665,"props":2755,"children":2756},{"style":688},[2757],{"type":510,"value":2758},", err));\n",{"type":504,"tag":665,"props":2760,"children":2761},{"class":667,"line":806},[2762],{"type":504,"tag":665,"props":2763,"children":2764},{"style":688},[2765],{"type":510,"value":2766},"  });\n",{"type":504,"tag":665,"props":2768,"children":2769},{"class":667,"line":843},[2770],{"type":504,"tag":665,"props":2771,"children":2772},{"style":688},[2773],{"type":510,"value":1337},{"type":504,"tag":544,"props":2775,"children":2777},{"id":2776},"c-使用-workbox",[2778],{"type":510,"value":2779},"C. 使用 Workbox",{"type":504,"tag":516,"props":2781,"children":2782},{},[2783,2785,2790],{"type":510,"value":2784},"手动编写和管理 Service Worker 很复杂。",{"type":504,"tag":520,"props":2786,"children":2787},{},[2788],{"type":510,"value":2789},"Workbox",{"type":510,"value":2791}," 是 Google 推出的一个库，它封装了大量最佳实践，极大地简化了 Service Worker 的开发。",{"type":504,"tag":563,"props":2793,"children":2794},{},[2795,2820],{"type":504,"tag":567,"props":2796,"children":2797},{},[2798,2803,2805,2811,2812,2818],{"type":504,"tag":520,"props":2799,"children":2800},{},[2801],{"type":510,"value":2802},"功能",{"type":510,"value":2804},"：提供路由管理、预缓存、多种缓存策略（如 ",{"type":504,"tag":661,"props":2806,"children":2808},{"className":2807},[],[2809],{"type":510,"value":2810},"CacheFirst",{"type":510,"value":778},{"type":504,"tag":661,"props":2813,"children":2815},{"className":2814},[],[2816],{"type":510,"value":2817},"NetworkFirst",{"type":510,"value":2819},"）、后台同步等高级功能。",{"type":504,"tag":567,"props":2821,"children":2822},{},[2823,2828],{"type":504,"tag":520,"props":2824,"children":2825},{},[2826],{"type":510,"value":2827},"集成",{"type":510,"value":2829},"：可以与 Webpack, Vite 等构建工具深度集成，自动生成 Service Worker 文件。",{"type":504,"tag":528,"props":2831,"children":2832},{},[],{"type":504,"tag":532,"props":2834,"children":2836},{"id":2835},"_4-关联阅读",[2837],{"type":510,"value":2838},"4. 关联阅读",{"type":504,"tag":563,"props":2840,"children":2841},{},[2842,2877,2900],{"type":504,"tag":567,"props":2843,"children":2844},{},[2845,2850,2852,2861,2862,2869,2870],{"type":504,"tag":520,"props":2846,"children":2847},{},[2848],{"type":510,"value":2849},"MDN 文档",{"type":510,"value":2851},": ",{"type":504,"tag":2853,"props":2854,"children":2858},"a",{"href":2855,"rel":2856},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWebSockets_API",[2857],"nofollow",[2859],{"type":510,"value":2860},"WebSocket",{"type":510,"value":778},{"type":504,"tag":2853,"props":2863,"children":2866},{"href":2864,"rel":2865},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FServer-sent_events\u002FUsing_server-sent_events",[2857],[2867],{"type":510,"value":2868},"Server-Sent Events",{"type":510,"value":778},{"type":504,"tag":2853,"props":2871,"children":2874},{"href":2872,"rel":2873},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FService_Worker_API",[2857],[2875],{"type":510,"value":2876},"Service Worker API",{"type":504,"tag":567,"props":2878,"children":2879},{},[2880,2885,2886,2893,2894],{"type":504,"tag":520,"props":2881,"children":2882},{},[2883],{"type":510,"value":2884},"Google web.dev",{"type":510,"value":2851},{"type":504,"tag":2853,"props":2887,"children":2890},{"href":2888,"rel":2889},"https:\u002F\u002Fweb.dev\u002Fprogressive-web-apps\u002F",[2857],[2891],{"type":510,"value":2892},"What is a PWA?",{"type":510,"value":778},{"type":504,"tag":2853,"props":2895,"children":2898},{"href":2896,"rel":2897},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox\u002F",[2857],[2899],{"type":510,"value":2789},{"type":504,"tag":567,"props":2901,"children":2902},{},[2903,2908,2910],{"type":504,"tag":520,"props":2904,"children":2905},{},[2906],{"type":510,"value":2907},"Socket.IO",{"type":510,"value":2909},": 一个流行的 WebSocket 库，提供了更好的抽象和回退机制 ",{"type":504,"tag":2853,"props":2911,"children":2914},{"href":2912,"rel":2913},"https:\u002F\u002Fsocket.io\u002F",[2857],[2915],{"type":510,"value":2916},"socket.io",{"type":504,"tag":2918,"props":2919,"children":2920},"style",{},[2921],{"type":510,"value":2922},"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":658,"searchDepth":678,"depth":678,"links":2924},[2925,2929,2934],{"id":534,"depth":678,"text":537,"children":2926},[2927,2928],{"id":546,"depth":710,"text":549},{"id":1340,"depth":710,"text":1343},{"id":1878,"depth":678,"text":1881,"children":2930},[2931,2932,2933],{"id":1884,"depth":710,"text":1887},{"id":1939,"depth":710,"text":1942},{"id":2776,"depth":710,"text":2779},{"id":2835,"depth":678,"text":2838},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":2936},"\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":2938},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":2940},"\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":2942,"height":2942,"rotate":4,"vFlip":6,"hFlip":6,"body":2943},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>"]