[{"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\u002F04-cicd-and-quality-gates":494,"i-ri:arrow-left-s-line":1869,"i-ri:menu-2-line":1871,"i-ri:arrow-right-s-line":1873,"i-ep:copy-document":1875},{"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":463,"path":463,"title":464,"updatedAt":21,"meta":498,"body":500,"toc":1858},"bm90ZTpub3Rlcy9yZWFjdC8xNS1lbmdpbmVlcmluZy1hbmQtYXJjaGl0ZWN0dXJlLzA0LWNpY2QtYW5kLXF1YWxpdHktZ2F0ZXMubWQ","note","notes\u002Freact\u002F15-engineering-and-architecture\u002F04-cicd-and-quality-gates.md",{"id":495,"type":496,"sourcePath":497,"slug":499,"title":464,"updatedAt":21,"lastUpdated":21},"react\u002F15-engineering-and-architecture\u002F04-cicd-and-quality-gates",{"type":501,"children":502},"root",[503,511,527,531,538,600,610,616,621,626,690,693,699,713,720,736,1493,1499,1605,1611,1713,1716,1722,1742,1778,1790,1793,1799,1852],{"type":504,"tag":505,"props":506,"children":508},"element","h1",{"id":507},"cicd-与质量门禁构建可靠的自动化流程",[509],{"type":510,"value":464},"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},"：通过 CI\u002FCD (持续集成\u002F持续部署) 管道，建立一套由 Lint、类型检查、测试和构建组成的自动化“质量门禁”，是确保代码在合并入主干前始终处于健康、可发布状态的关键工程实践。",{"type":504,"tag":528,"props":529,"children":530},"hr",{},[],{"type":504,"tag":532,"props":533,"children":535},"h2",{"id":534},"_1-什么是-cicd",[536],{"type":510,"value":537},"1. 什么是 CI\u002FCD？",{"type":504,"tag":539,"props":540,"children":541},"ul",{},[542,560],{"type":504,"tag":543,"props":544,"children":545},"li",{},[546,551,553,558],{"type":504,"tag":520,"props":547,"children":548},{},[549],{"type":510,"value":550},"持续集成 (Continuous Integration - CI)",{"type":510,"value":552},"：一种开发实践，团队成员频繁地将其代码变更集成到共享的主干仓库中。每次集成都会通过",{"type":504,"tag":520,"props":554,"children":555},{},[556],{"type":510,"value":557},"自动化构建和测试",{"type":510,"value":559},"来验证，从而尽早地发现集成错误。",{"type":504,"tag":543,"props":561,"children":562},{},[563,568,570],{"type":504,"tag":520,"props":564,"children":565},{},[566],{"type":510,"value":567},"持续部署\u002F交付 (Continuous Deployment\u002FDelivery - CD)",{"type":510,"value":569},"：\n",{"type":504,"tag":539,"props":571,"children":572},{},[573,583],{"type":504,"tag":543,"props":574,"children":575},{},[576,581],{"type":504,"tag":520,"props":577,"children":578},{},[579],{"type":510,"value":580},"持续交付",{"type":510,"value":582},"：在 CI 的基础上，将通过所有测试的代码自动发布到一个“准生产”环境，以供手动批准部署到生产环境。",{"type":504,"tag":543,"props":584,"children":585},{},[586,591,593,598],{"type":504,"tag":520,"props":587,"children":588},{},[589],{"type":510,"value":590},"持续部署",{"type":510,"value":592},"：更进一步，将通过所有测试的代码",{"type":504,"tag":520,"props":594,"children":595},{},[596],{"type":510,"value":597},"自动部署",{"type":510,"value":599},"到生产环境。",{"type":504,"tag":516,"props":601,"children":602},{},[603,608],{"type":504,"tag":520,"props":604,"children":605},{},[606],{"type":510,"value":607},"核心目标",{"type":510,"value":609},"：实现更快、更可靠、风险更低的软件交付。",{"type":504,"tag":532,"props":611,"children":613},{"id":612},"_2-自动化质量门禁-the-quality-gates",[614],{"type":510,"value":615},"2. 自动化质量门禁 (The Quality Gates)",{"type":504,"tag":516,"props":617,"children":618},{},[619],{"type":510,"value":620},"质量门禁是在 CI 流程中设置的一系列自动化检查关卡。只有当代码变更逐一通过所有关卡时，才被允许合并到主分支。",{"type":504,"tag":516,"props":622,"children":623},{},[624],{"type":510,"value":625},"一个典型的 React\u002FTypeScript 项目的质量门禁流水线如下：",{"type":504,"tag":627,"props":628,"children":629},"ol",{},[630,640,650,660,670,680],{"type":504,"tag":543,"props":631,"children":632},{},[633,638],{"type":504,"tag":520,"props":634,"children":635},{},[636],{"type":510,"value":637},"Linting (代码风格检查)",{"type":510,"value":639},"：确保代码遵循团队统一的编码规范。",{"type":504,"tag":543,"props":641,"children":642},{},[643,648],{"type":504,"tag":520,"props":644,"children":645},{},[646],{"type":510,"value":647},"Type Checking (类型检查)",{"type":510,"value":649},"：利用 TypeScript 捕获类型错误。",{"type":504,"tag":543,"props":651,"children":652},{},[653,658],{"type":504,"tag":520,"props":654,"children":655},{},[656],{"type":510,"value":657},"Unit & Integration Testing (单元\u002F集成测试)",{"type":510,"value":659},"：验证代码逻辑和组件交互。",{"type":504,"tag":543,"props":661,"children":662},{},[663,668],{"type":504,"tag":520,"props":664,"children":665},{},[666],{"type":510,"value":667},"E2E Testing (端到端测试)",{"type":510,"value":669},"：验证关键用户流程。",{"type":504,"tag":543,"props":671,"children":672},{},[673,678],{"type":504,"tag":520,"props":674,"children":675},{},[676],{"type":510,"value":677},"Build (构建)",{"type":510,"value":679},"：确保应用可以成功打包。",{"type":504,"tag":543,"props":681,"children":682},{},[683,688],{"type":504,"tag":520,"props":684,"children":685},{},[686],{"type":510,"value":687},"(可选) Preview Deployment (预览部署)",{"type":510,"value":689},"：为每个 Pull Request 创建一个临时的、可公开访问的应用实例，方便 UI\u002FUX 和产品经理进行审查。",{"type":504,"tag":528,"props":691,"children":692},{},[],{"type":504,"tag":532,"props":694,"children":696},{"id":695},"_3-使用-github-actions-实现-ci",[697],{"type":510,"value":698},"3. 使用 GitHub Actions 实现 CI",{"type":504,"tag":516,"props":700,"children":701},{},[702,704,711],{"type":510,"value":703},"GitHub Actions 是一个内置于 GitHub 的强大 CI\u002FCD 工具。你通过在仓库的 ",{"type":504,"tag":705,"props":706,"children":708},"code",{"className":707},[],[709],{"type":510,"value":710},".github\u002Fworkflows\u002F",{"type":510,"value":712}," 目录下创建 YAML 文件来定义工作流。",{"type":504,"tag":714,"props":715,"children":717},"h3",{"id":716},"示例一个完整的前端-ci-工作流",[718],{"type":510,"value":719},"示例：一个完整的前端 CI 工作流",{"type":504,"tag":516,"props":721,"children":722},{},[723,728,730],{"type":504,"tag":520,"props":724,"children":725},{},[726],{"type":510,"value":727},"文件名",{"type":510,"value":729},": ",{"type":504,"tag":705,"props":731,"children":733},{"className":732},[],[734],{"type":510,"value":735},".github\u002Fworkflows\u002Fci.yml",{"type":504,"tag":737,"props":738,"children":743},"pre",{"className":739,"code":740,"language":741,"meta":742,"style":742},"language-yaml shiki shiki-themes github-light github-dark","name: CI\n\n# 触发条件：当有代码 push 到 main 分支，或创建\u002F更新一个 Pull Request 时\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  # 定义一个名为 \"quality-gates\" 的 job\n  quality-gates:\n    # 运行环境：使用最新的 Ubuntu 系统\n    runs-on: ubuntu-latest\n\n    # 定义一系列步骤\n    steps:\n      # 步骤 1: 检出代码\n      - name: Checkout repository\n        uses: actions\u002Fcheckout@v3\n\n      # 步骤 2: 设置 Node.js 环境\n      # 使用 pnpm 作为包管理器，并利用其缓存机制\n      - name: Setup Node.js and pnpm\n        uses: pnpm\u002Faction-setup@v2\n        with:\n          version: 8 # pnpm 版本\n          node-version: '18' # Node.js 版本\n\n      # 步骤 3: 安装依赖\n      # Turborepo 会自动利用其远程缓存\n      - name: Install dependencies\n        run: pnpm install\n\n      # 步骤 4: 质量门禁 - Lint\n      - name: Run Lint\n        run: pnpm lint\n\n      # 步骤 5: 质量门禁 - Type Check\n      - name: Run Type Check\n        # 在 monorepo 中，通常需要对每个包分别进行类型检查\n        # 假设 turbo.json 中配置了 lint 和 typecheck 任务\n        run: pnpm typecheck\n\n      # 步骤 6: 质量门禁 - Unit & Integration Tests\n      - name: Run Tests\n        run: pnpm test\n\n      # 步骤 7: 质量门禁 - Build\n      - name: Run Build\n        run: pnpm build\n\n      # (可选) 步骤 8: 质量门禁 - E2E Tests\n      # E2E 测试通常更耗时，可以放在一个独立的 job 中并行执行\n      # - name: Run E2E Tests\n      #   run: pnpm test:e2e\n","yaml","",[744],{"type":504,"tag":705,"props":745,"children":746},{"__ignoreMap":742},[747,770,780,790,805,818,842,855,875,883,896,905,918,927,945,953,962,975,984,1006,1024,1032,1041,1050,1070,1087,1100,1123,1146,1154,1163,1172,1193,1211,1219,1228,1249,1266,1274,1283,1304,1313,1322,1339,1347,1356,1377,1394,1402,1411,1432,1449,1457,1466,1475,1484],{"type":504,"tag":748,"props":749,"children":752},"span",{"class":750,"line":751},"line",1,[753,759,764],{"type":504,"tag":748,"props":754,"children":756},{"style":755},"--shiki-default:#22863A;--shiki-dark:#85E89D",[757],{"type":510,"value":758},"name",{"type":504,"tag":748,"props":760,"children":762},{"style":761},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[763],{"type":510,"value":729},{"type":504,"tag":748,"props":765,"children":767},{"style":766},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[768],{"type":510,"value":769},"CI\n",{"type":504,"tag":748,"props":771,"children":773},{"class":750,"line":772},2,[774],{"type":504,"tag":748,"props":775,"children":777},{"emptyLinePlaceholder":776},true,[778],{"type":510,"value":779},"\n",{"type":504,"tag":748,"props":781,"children":783},{"class":750,"line":782},3,[784],{"type":504,"tag":748,"props":785,"children":787},{"style":786},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[788],{"type":510,"value":789},"# 触发条件：当有代码 push 到 main 分支，或创建\u002F更新一个 Pull Request 时\n",{"type":504,"tag":748,"props":791,"children":793},{"class":750,"line":792},4,[794,800],{"type":504,"tag":748,"props":795,"children":797},{"style":796},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[798],{"type":510,"value":799},"on",{"type":504,"tag":748,"props":801,"children":802},{"style":761},[803],{"type":510,"value":804},":\n",{"type":504,"tag":748,"props":806,"children":808},{"class":750,"line":807},5,[809,814],{"type":504,"tag":748,"props":810,"children":811},{"style":755},[812],{"type":510,"value":813},"  push",{"type":504,"tag":748,"props":815,"children":816},{"style":761},[817],{"type":510,"value":804},{"type":504,"tag":748,"props":819,"children":821},{"class":750,"line":820},6,[822,827,832,837],{"type":504,"tag":748,"props":823,"children":824},{"style":755},[825],{"type":510,"value":826},"    branches",{"type":504,"tag":748,"props":828,"children":829},{"style":761},[830],{"type":510,"value":831},": [",{"type":504,"tag":748,"props":833,"children":834},{"style":766},[835],{"type":510,"value":836},"main",{"type":504,"tag":748,"props":838,"children":839},{"style":761},[840],{"type":510,"value":841},"]\n",{"type":504,"tag":748,"props":843,"children":845},{"class":750,"line":844},7,[846,851],{"type":504,"tag":748,"props":847,"children":848},{"style":755},[849],{"type":510,"value":850},"  pull_request",{"type":504,"tag":748,"props":852,"children":853},{"style":761},[854],{"type":510,"value":804},{"type":504,"tag":748,"props":856,"children":858},{"class":750,"line":857},8,[859,863,867,871],{"type":504,"tag":748,"props":860,"children":861},{"style":755},[862],{"type":510,"value":826},{"type":504,"tag":748,"props":864,"children":865},{"style":761},[866],{"type":510,"value":831},{"type":504,"tag":748,"props":868,"children":869},{"style":766},[870],{"type":510,"value":836},{"type":504,"tag":748,"props":872,"children":873},{"style":761},[874],{"type":510,"value":841},{"type":504,"tag":748,"props":876,"children":878},{"class":750,"line":877},9,[879],{"type":504,"tag":748,"props":880,"children":881},{"emptyLinePlaceholder":776},[882],{"type":510,"value":779},{"type":504,"tag":748,"props":884,"children":886},{"class":750,"line":885},10,[887,892],{"type":504,"tag":748,"props":888,"children":889},{"style":755},[890],{"type":510,"value":891},"jobs",{"type":504,"tag":748,"props":893,"children":894},{"style":761},[895],{"type":510,"value":804},{"type":504,"tag":748,"props":897,"children":899},{"class":750,"line":898},11,[900],{"type":504,"tag":748,"props":901,"children":902},{"style":786},[903],{"type":510,"value":904},"  # 定义一个名为 \"quality-gates\" 的 job\n",{"type":504,"tag":748,"props":906,"children":908},{"class":750,"line":907},12,[909,914],{"type":504,"tag":748,"props":910,"children":911},{"style":755},[912],{"type":510,"value":913},"  quality-gates",{"type":504,"tag":748,"props":915,"children":916},{"style":761},[917],{"type":510,"value":804},{"type":504,"tag":748,"props":919,"children":921},{"class":750,"line":920},13,[922],{"type":504,"tag":748,"props":923,"children":924},{"style":786},[925],{"type":510,"value":926},"    # 运行环境：使用最新的 Ubuntu 系统\n",{"type":504,"tag":748,"props":928,"children":930},{"class":750,"line":929},14,[931,936,940],{"type":504,"tag":748,"props":932,"children":933},{"style":755},[934],{"type":510,"value":935},"    runs-on",{"type":504,"tag":748,"props":937,"children":938},{"style":761},[939],{"type":510,"value":729},{"type":504,"tag":748,"props":941,"children":942},{"style":766},[943],{"type":510,"value":944},"ubuntu-latest\n",{"type":504,"tag":748,"props":946,"children":948},{"class":750,"line":947},15,[949],{"type":504,"tag":748,"props":950,"children":951},{"emptyLinePlaceholder":776},[952],{"type":510,"value":779},{"type":504,"tag":748,"props":954,"children":956},{"class":750,"line":955},16,[957],{"type":504,"tag":748,"props":958,"children":959},{"style":786},[960],{"type":510,"value":961},"    # 定义一系列步骤\n",{"type":504,"tag":748,"props":963,"children":965},{"class":750,"line":964},17,[966,971],{"type":504,"tag":748,"props":967,"children":968},{"style":755},[969],{"type":510,"value":970},"    steps",{"type":504,"tag":748,"props":972,"children":973},{"style":761},[974],{"type":510,"value":804},{"type":504,"tag":748,"props":976,"children":978},{"class":750,"line":977},18,[979],{"type":504,"tag":748,"props":980,"children":981},{"style":786},[982],{"type":510,"value":983},"      # 步骤 1: 检出代码\n",{"type":504,"tag":748,"props":985,"children":987},{"class":750,"line":986},19,[988,993,997,1001],{"type":504,"tag":748,"props":989,"children":990},{"style":761},[991],{"type":510,"value":992},"      - ",{"type":504,"tag":748,"props":994,"children":995},{"style":755},[996],{"type":510,"value":758},{"type":504,"tag":748,"props":998,"children":999},{"style":761},[1000],{"type":510,"value":729},{"type":504,"tag":748,"props":1002,"children":1003},{"style":766},[1004],{"type":510,"value":1005},"Checkout repository\n",{"type":504,"tag":748,"props":1007,"children":1009},{"class":750,"line":1008},20,[1010,1015,1019],{"type":504,"tag":748,"props":1011,"children":1012},{"style":755},[1013],{"type":510,"value":1014},"        uses",{"type":504,"tag":748,"props":1016,"children":1017},{"style":761},[1018],{"type":510,"value":729},{"type":504,"tag":748,"props":1020,"children":1021},{"style":766},[1022],{"type":510,"value":1023},"actions\u002Fcheckout@v3\n",{"type":504,"tag":748,"props":1025,"children":1027},{"class":750,"line":1026},21,[1028],{"type":504,"tag":748,"props":1029,"children":1030},{"emptyLinePlaceholder":776},[1031],{"type":510,"value":779},{"type":504,"tag":748,"props":1033,"children":1035},{"class":750,"line":1034},22,[1036],{"type":504,"tag":748,"props":1037,"children":1038},{"style":786},[1039],{"type":510,"value":1040},"      # 步骤 2: 设置 Node.js 环境\n",{"type":504,"tag":748,"props":1042,"children":1044},{"class":750,"line":1043},23,[1045],{"type":504,"tag":748,"props":1046,"children":1047},{"style":786},[1048],{"type":510,"value":1049},"      # 使用 pnpm 作为包管理器，并利用其缓存机制\n",{"type":504,"tag":748,"props":1051,"children":1052},{"class":750,"line":5},[1053,1057,1061,1065],{"type":504,"tag":748,"props":1054,"children":1055},{"style":761},[1056],{"type":510,"value":992},{"type":504,"tag":748,"props":1058,"children":1059},{"style":755},[1060],{"type":510,"value":758},{"type":504,"tag":748,"props":1062,"children":1063},{"style":761},[1064],{"type":510,"value":729},{"type":504,"tag":748,"props":1066,"children":1067},{"style":766},[1068],{"type":510,"value":1069},"Setup Node.js and pnpm\n",{"type":504,"tag":748,"props":1071,"children":1073},{"class":750,"line":1072},25,[1074,1078,1082],{"type":504,"tag":748,"props":1075,"children":1076},{"style":755},[1077],{"type":510,"value":1014},{"type":504,"tag":748,"props":1079,"children":1080},{"style":761},[1081],{"type":510,"value":729},{"type":504,"tag":748,"props":1083,"children":1084},{"style":766},[1085],{"type":510,"value":1086},"pnpm\u002Faction-setup@v2\n",{"type":504,"tag":748,"props":1088,"children":1090},{"class":750,"line":1089},26,[1091,1096],{"type":504,"tag":748,"props":1092,"children":1093},{"style":755},[1094],{"type":510,"value":1095},"        with",{"type":504,"tag":748,"props":1097,"children":1098},{"style":761},[1099],{"type":510,"value":804},{"type":504,"tag":748,"props":1101,"children":1103},{"class":750,"line":1102},27,[1104,1109,1113,1118],{"type":504,"tag":748,"props":1105,"children":1106},{"style":755},[1107],{"type":510,"value":1108},"          version",{"type":504,"tag":748,"props":1110,"children":1111},{"style":761},[1112],{"type":510,"value":729},{"type":504,"tag":748,"props":1114,"children":1115},{"style":796},[1116],{"type":510,"value":1117},"8",{"type":504,"tag":748,"props":1119,"children":1120},{"style":786},[1121],{"type":510,"value":1122}," # pnpm 版本\n",{"type":504,"tag":748,"props":1124,"children":1126},{"class":750,"line":1125},28,[1127,1132,1136,1141],{"type":504,"tag":748,"props":1128,"children":1129},{"style":755},[1130],{"type":510,"value":1131},"          node-version",{"type":504,"tag":748,"props":1133,"children":1134},{"style":761},[1135],{"type":510,"value":729},{"type":504,"tag":748,"props":1137,"children":1138},{"style":766},[1139],{"type":510,"value":1140},"'18'",{"type":504,"tag":748,"props":1142,"children":1143},{"style":786},[1144],{"type":510,"value":1145}," # Node.js 版本\n",{"type":504,"tag":748,"props":1147,"children":1149},{"class":750,"line":1148},29,[1150],{"type":504,"tag":748,"props":1151,"children":1152},{"emptyLinePlaceholder":776},[1153],{"type":510,"value":779},{"type":504,"tag":748,"props":1155,"children":1157},{"class":750,"line":1156},30,[1158],{"type":504,"tag":748,"props":1159,"children":1160},{"style":786},[1161],{"type":510,"value":1162},"      # 步骤 3: 安装依赖\n",{"type":504,"tag":748,"props":1164,"children":1166},{"class":750,"line":1165},31,[1167],{"type":504,"tag":748,"props":1168,"children":1169},{"style":786},[1170],{"type":510,"value":1171},"      # Turborepo 会自动利用其远程缓存\n",{"type":504,"tag":748,"props":1173,"children":1175},{"class":750,"line":1174},32,[1176,1180,1184,1188],{"type":504,"tag":748,"props":1177,"children":1178},{"style":761},[1179],{"type":510,"value":992},{"type":504,"tag":748,"props":1181,"children":1182},{"style":755},[1183],{"type":510,"value":758},{"type":504,"tag":748,"props":1185,"children":1186},{"style":761},[1187],{"type":510,"value":729},{"type":504,"tag":748,"props":1189,"children":1190},{"style":766},[1191],{"type":510,"value":1192},"Install dependencies\n",{"type":504,"tag":748,"props":1194,"children":1196},{"class":750,"line":1195},33,[1197,1202,1206],{"type":504,"tag":748,"props":1198,"children":1199},{"style":755},[1200],{"type":510,"value":1201},"        run",{"type":504,"tag":748,"props":1203,"children":1204},{"style":761},[1205],{"type":510,"value":729},{"type":504,"tag":748,"props":1207,"children":1208},{"style":766},[1209],{"type":510,"value":1210},"pnpm install\n",{"type":504,"tag":748,"props":1212,"children":1214},{"class":750,"line":1213},34,[1215],{"type":504,"tag":748,"props":1216,"children":1217},{"emptyLinePlaceholder":776},[1218],{"type":510,"value":779},{"type":504,"tag":748,"props":1220,"children":1222},{"class":750,"line":1221},35,[1223],{"type":504,"tag":748,"props":1224,"children":1225},{"style":786},[1226],{"type":510,"value":1227},"      # 步骤 4: 质量门禁 - Lint\n",{"type":504,"tag":748,"props":1229,"children":1231},{"class":750,"line":1230},36,[1232,1236,1240,1244],{"type":504,"tag":748,"props":1233,"children":1234},{"style":761},[1235],{"type":510,"value":992},{"type":504,"tag":748,"props":1237,"children":1238},{"style":755},[1239],{"type":510,"value":758},{"type":504,"tag":748,"props":1241,"children":1242},{"style":761},[1243],{"type":510,"value":729},{"type":504,"tag":748,"props":1245,"children":1246},{"style":766},[1247],{"type":510,"value":1248},"Run Lint\n",{"type":504,"tag":748,"props":1250,"children":1252},{"class":750,"line":1251},37,[1253,1257,1261],{"type":504,"tag":748,"props":1254,"children":1255},{"style":755},[1256],{"type":510,"value":1201},{"type":504,"tag":748,"props":1258,"children":1259},{"style":761},[1260],{"type":510,"value":729},{"type":504,"tag":748,"props":1262,"children":1263},{"style":766},[1264],{"type":510,"value":1265},"pnpm lint\n",{"type":504,"tag":748,"props":1267,"children":1269},{"class":750,"line":1268},38,[1270],{"type":504,"tag":748,"props":1271,"children":1272},{"emptyLinePlaceholder":776},[1273],{"type":510,"value":779},{"type":504,"tag":748,"props":1275,"children":1277},{"class":750,"line":1276},39,[1278],{"type":504,"tag":748,"props":1279,"children":1280},{"style":786},[1281],{"type":510,"value":1282},"      # 步骤 5: 质量门禁 - Type Check\n",{"type":504,"tag":748,"props":1284,"children":1286},{"class":750,"line":1285},40,[1287,1291,1295,1299],{"type":504,"tag":748,"props":1288,"children":1289},{"style":761},[1290],{"type":510,"value":992},{"type":504,"tag":748,"props":1292,"children":1293},{"style":755},[1294],{"type":510,"value":758},{"type":504,"tag":748,"props":1296,"children":1297},{"style":761},[1298],{"type":510,"value":729},{"type":504,"tag":748,"props":1300,"children":1301},{"style":766},[1302],{"type":510,"value":1303},"Run Type Check\n",{"type":504,"tag":748,"props":1305,"children":1307},{"class":750,"line":1306},41,[1308],{"type":504,"tag":748,"props":1309,"children":1310},{"style":786},[1311],{"type":510,"value":1312},"        # 在 monorepo 中，通常需要对每个包分别进行类型检查\n",{"type":504,"tag":748,"props":1314,"children":1316},{"class":750,"line":1315},42,[1317],{"type":504,"tag":748,"props":1318,"children":1319},{"style":786},[1320],{"type":510,"value":1321},"        # 假设 turbo.json 中配置了 lint 和 typecheck 任务\n",{"type":504,"tag":748,"props":1323,"children":1325},{"class":750,"line":1324},43,[1326,1330,1334],{"type":504,"tag":748,"props":1327,"children":1328},{"style":755},[1329],{"type":510,"value":1201},{"type":504,"tag":748,"props":1331,"children":1332},{"style":761},[1333],{"type":510,"value":729},{"type":504,"tag":748,"props":1335,"children":1336},{"style":766},[1337],{"type":510,"value":1338},"pnpm typecheck\n",{"type":504,"tag":748,"props":1340,"children":1342},{"class":750,"line":1341},44,[1343],{"type":504,"tag":748,"props":1344,"children":1345},{"emptyLinePlaceholder":776},[1346],{"type":510,"value":779},{"type":504,"tag":748,"props":1348,"children":1350},{"class":750,"line":1349},45,[1351],{"type":504,"tag":748,"props":1352,"children":1353},{"style":786},[1354],{"type":510,"value":1355},"      # 步骤 6: 质量门禁 - Unit & Integration Tests\n",{"type":504,"tag":748,"props":1357,"children":1359},{"class":750,"line":1358},46,[1360,1364,1368,1372],{"type":504,"tag":748,"props":1361,"children":1362},{"style":761},[1363],{"type":510,"value":992},{"type":504,"tag":748,"props":1365,"children":1366},{"style":755},[1367],{"type":510,"value":758},{"type":504,"tag":748,"props":1369,"children":1370},{"style":761},[1371],{"type":510,"value":729},{"type":504,"tag":748,"props":1373,"children":1374},{"style":766},[1375],{"type":510,"value":1376},"Run Tests\n",{"type":504,"tag":748,"props":1378,"children":1380},{"class":750,"line":1379},47,[1381,1385,1389],{"type":504,"tag":748,"props":1382,"children":1383},{"style":755},[1384],{"type":510,"value":1201},{"type":504,"tag":748,"props":1386,"children":1387},{"style":761},[1388],{"type":510,"value":729},{"type":504,"tag":748,"props":1390,"children":1391},{"style":766},[1392],{"type":510,"value":1393},"pnpm test\n",{"type":504,"tag":748,"props":1395,"children":1397},{"class":750,"line":1396},48,[1398],{"type":504,"tag":748,"props":1399,"children":1400},{"emptyLinePlaceholder":776},[1401],{"type":510,"value":779},{"type":504,"tag":748,"props":1403,"children":1405},{"class":750,"line":1404},49,[1406],{"type":504,"tag":748,"props":1407,"children":1408},{"style":786},[1409],{"type":510,"value":1410},"      # 步骤 7: 质量门禁 - Build\n",{"type":504,"tag":748,"props":1412,"children":1414},{"class":750,"line":1413},50,[1415,1419,1423,1427],{"type":504,"tag":748,"props":1416,"children":1417},{"style":761},[1418],{"type":510,"value":992},{"type":504,"tag":748,"props":1420,"children":1421},{"style":755},[1422],{"type":510,"value":758},{"type":504,"tag":748,"props":1424,"children":1425},{"style":761},[1426],{"type":510,"value":729},{"type":504,"tag":748,"props":1428,"children":1429},{"style":766},[1430],{"type":510,"value":1431},"Run Build\n",{"type":504,"tag":748,"props":1433,"children":1435},{"class":750,"line":1434},51,[1436,1440,1444],{"type":504,"tag":748,"props":1437,"children":1438},{"style":755},[1439],{"type":510,"value":1201},{"type":504,"tag":748,"props":1441,"children":1442},{"style":761},[1443],{"type":510,"value":729},{"type":504,"tag":748,"props":1445,"children":1446},{"style":766},[1447],{"type":510,"value":1448},"pnpm build\n",{"type":504,"tag":748,"props":1450,"children":1452},{"class":750,"line":1451},52,[1453],{"type":504,"tag":748,"props":1454,"children":1455},{"emptyLinePlaceholder":776},[1456],{"type":510,"value":779},{"type":504,"tag":748,"props":1458,"children":1460},{"class":750,"line":1459},53,[1461],{"type":504,"tag":748,"props":1462,"children":1463},{"style":786},[1464],{"type":510,"value":1465},"      # (可选) 步骤 8: 质量门禁 - E2E Tests\n",{"type":504,"tag":748,"props":1467,"children":1469},{"class":750,"line":1468},54,[1470],{"type":504,"tag":748,"props":1471,"children":1472},{"style":786},[1473],{"type":510,"value":1474},"      # E2E 测试通常更耗时，可以放在一个独立的 job 中并行执行\n",{"type":504,"tag":748,"props":1476,"children":1478},{"class":750,"line":1477},55,[1479],{"type":504,"tag":748,"props":1480,"children":1481},{"style":786},[1482],{"type":510,"value":1483},"      # - name: Run E2E Tests\n",{"type":504,"tag":748,"props":1485,"children":1487},{"class":750,"line":1486},56,[1488],{"type":504,"tag":748,"props":1489,"children":1490},{"style":786},[1491],{"type":510,"value":1492},"      #   run: pnpm test:e2e\n",{"type":504,"tag":714,"props":1494,"children":1496},{"id":1495},"工作流解析",[1497],{"type":510,"value":1498},"工作流解析：",{"type":504,"tag":539,"props":1500,"children":1501},{},[1502,1535,1545,1556,1567,1594],{"type":504,"tag":543,"props":1503,"children":1504},{},[1505,1510,1512,1517,1519,1525,1527,1533],{"type":504,"tag":705,"props":1506,"children":1508},{"className":1507},[],[1509],{"type":510,"value":799},{"type":510,"value":1511},": 定义了触发工作流的事件。这里是针对 ",{"type":504,"tag":705,"props":1513,"children":1515},{"className":1514},[],[1516],{"type":510,"value":836},{"type":510,"value":1518}," 分支的 ",{"type":504,"tag":705,"props":1520,"children":1522},{"className":1521},[],[1523],{"type":510,"value":1524},"push",{"type":510,"value":1526}," 和 ",{"type":504,"tag":705,"props":1528,"children":1530},{"className":1529},[],[1531],{"type":510,"value":1532},"pull_request",{"type":510,"value":1534},"。",{"type":504,"tag":543,"props":1536,"children":1537},{},[1538,1543],{"type":504,"tag":705,"props":1539,"children":1541},{"className":1540},[],[1542],{"type":510,"value":891},{"type":510,"value":1544},": 一个工作流可以包含一个或多个 job。它们可以并行或串行执行。",{"type":504,"tag":543,"props":1546,"children":1547},{},[1548,1554],{"type":504,"tag":705,"props":1549,"children":1551},{"className":1550},[],[1552],{"type":510,"value":1553},"runs-on",{"type":510,"value":1555},": 指定了运行 job 的虚拟机环境。",{"type":504,"tag":543,"props":1557,"children":1558},{},[1559,1565],{"type":504,"tag":705,"props":1560,"children":1562},{"className":1561},[],[1563],{"type":510,"value":1564},"steps",{"type":510,"value":1566},": 一个 job 由多个步骤组成，它们会按顺序执行。",{"type":504,"tag":543,"props":1568,"children":1569},{},[1570,1576,1578,1584,1586,1592],{"type":504,"tag":705,"props":1571,"children":1573},{"className":1572},[],[1574],{"type":510,"value":1575},"uses",{"type":510,"value":1577},": 使用社区或官方提供的 Action，例如 ",{"type":504,"tag":705,"props":1579,"children":1581},{"className":1580},[],[1582],{"type":510,"value":1583},"actions\u002Fcheckout",{"type":510,"value":1585}," 用于拉取代码，",{"type":504,"tag":705,"props":1587,"children":1589},{"className":1588},[],[1590],{"type":510,"value":1591},"pnpm\u002Faction-setup",{"type":510,"value":1593}," 用于设置 pnpm。",{"type":504,"tag":543,"props":1595,"children":1596},{},[1597,1603],{"type":504,"tag":705,"props":1598,"children":1600},{"className":1599},[],[1601],{"type":510,"value":1602},"run",{"type":510,"value":1604},": 执行命令行指令。",{"type":504,"tag":714,"props":1606,"children":1608},{"id":1607},"结合-turborepo-和缓存",[1609],{"type":510,"value":1610},"结合 Turborepo 和缓存",{"type":504,"tag":539,"props":1612,"children":1613},{},[1614,1639],{"type":504,"tag":543,"props":1615,"children":1616},{},[1617,1622,1624,1629,1631,1637],{"type":504,"tag":520,"props":1618,"children":1619},{},[1620],{"type":510,"value":1621},"pnpm 缓存",{"type":510,"value":1623},"：",{"type":504,"tag":705,"props":1625,"children":1627},{"className":1626},[],[1628],{"type":510,"value":1591},{"type":510,"value":1630}," 会自动配置 pnpm 的内容寻址存储缓存，极大地加快了 ",{"type":504,"tag":705,"props":1632,"children":1634},{"className":1633},[],[1635],{"type":510,"value":1636},"pnpm install",{"type":510,"value":1638}," 的速度。",{"type":504,"tag":543,"props":1640,"children":1641},{},[1642,1647,1648],{"type":504,"tag":520,"props":1643,"children":1644},{},[1645],{"type":510,"value":1646},"Turborepo 远程缓存",{"type":510,"value":569},{"type":504,"tag":627,"props":1649,"children":1650},{},[1651,1656,1684],{"type":504,"tag":543,"props":1652,"children":1653},{},[1654],{"type":510,"value":1655},"在 Turborepo 的官网 (Vercel) 上创建一个账号并关联你的仓库。",{"type":504,"tag":543,"props":1657,"children":1658},{},[1659,1661,1667,1669,1675,1676,1682],{"type":510,"value":1660},"在仓库的 ",{"type":504,"tag":705,"props":1662,"children":1664},{"className":1663},[],[1665],{"type":510,"value":1666},"Settings > Secrets and variables > Actions",{"type":510,"value":1668}," 中，添加 ",{"type":504,"tag":705,"props":1670,"children":1672},{"className":1671},[],[1673],{"type":510,"value":1674},"TURBO_TOKEN",{"type":510,"value":1526},{"type":504,"tag":705,"props":1677,"children":1679},{"className":1678},[],[1680],{"type":510,"value":1681},"TURBO_TEAM",{"type":510,"value":1683}," 这两个 secret。",{"type":504,"tag":543,"props":1685,"children":1686},{},[1687,1689,1695,1697,1703,1705,1711],{"type":510,"value":1688},"现在，当 GitHub Actions 运行 ",{"type":504,"tag":705,"props":1690,"children":1692},{"className":1691},[],[1693],{"type":510,"value":1694},"pnpm build",{"type":510,"value":1696}," 或 ",{"type":504,"tag":705,"props":1698,"children":1700},{"className":1699},[],[1701],{"type":510,"value":1702},"pnpm test",{"type":510,"value":1704}," 时，Turborepo 会自动连接到远程缓存。如果某个任务的输入没有变化，它会直接下载缓存的产物（例如 ",{"type":504,"tag":705,"props":1706,"children":1708},{"className":1707},[],[1709],{"type":510,"value":1710},"dist",{"type":510,"value":1712}," 目录）和日志，而不是在 CI 服务器上重新执行，从而将 CI 时间从几十分钟缩短到几分钟甚至几十秒。",{"type":504,"tag":528,"props":1714,"children":1715},{},[],{"type":504,"tag":532,"props":1717,"children":1719},{"id":1718},"_4-保护主分支-branch-protection-rules",[1720],{"type":510,"value":1721},"4. 保护主分支 (Branch Protection Rules)",{"type":504,"tag":516,"props":1723,"children":1724},{},[1725,1727,1733,1735,1740],{"type":510,"value":1726},"最后一步是强制执行你的质量门禁。在 GitHub 仓库的 ",{"type":504,"tag":705,"props":1728,"children":1730},{"className":1729},[],[1731],{"type":510,"value":1732},"Settings > Branches",{"type":510,"value":1734}," 中，为你的 ",{"type":504,"tag":705,"props":1736,"children":1738},{"className":1737},[],[1739],{"type":510,"value":836},{"type":510,"value":1741}," 分支添加保护规则：",{"type":504,"tag":627,"props":1743,"children":1744},{},[1745,1755,1765],{"type":504,"tag":543,"props":1746,"children":1747},{},[1748,1753],{"type":504,"tag":520,"props":1749,"children":1750},{},[1751],{"type":510,"value":1752},"Require a pull request before merging",{"type":510,"value":1754},": 强制所有变更都必须通过 PR。",{"type":504,"tag":543,"props":1756,"children":1757},{},[1758,1763],{"type":504,"tag":520,"props":1759,"children":1760},{},[1761],{"type":510,"value":1762},"Require status checks to pass before merging",{"type":510,"value":1764},": 勾选此项。",{"type":504,"tag":543,"props":1766,"children":1767},{},[1768,1770,1776],{"type":510,"value":1769},"在下方的状态检查列表中，选择你的 CI job (例如 ",{"type":504,"tag":705,"props":1771,"children":1773},{"className":1772},[],[1774],{"type":510,"value":1775},"quality-gates",{"type":510,"value":1777},")。",{"type":504,"tag":516,"props":1779,"children":1780},{},[1781,1783,1788],{"type":510,"value":1782},"设置完成后，任何未通过 ",{"type":504,"tag":705,"props":1784,"children":1786},{"className":1785},[],[1787],{"type":510,"value":1775},{"type":510,"value":1789}," job 检查的 PR 都将无法被合并，从而形成了一个完整的、自动化的质量保障闭环。",{"type":504,"tag":528,"props":1791,"children":1792},{},[],{"type":504,"tag":532,"props":1794,"children":1796},{"id":1795},"_5-关联阅读",[1797],{"type":510,"value":1798},"5. 关联阅读",{"type":504,"tag":539,"props":1800,"children":1801},{},[1802,1820,1836],{"type":504,"tag":543,"props":1803,"children":1804},{},[1805,1810,1811],{"type":504,"tag":520,"props":1806,"children":1807},{},[1808],{"type":510,"value":1809},"GitHub Actions 官方文档",{"type":510,"value":729},{"type":504,"tag":1812,"props":1813,"children":1817},"a",{"href":1814,"rel":1815},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions",[1816],"nofollow",[1818],{"type":510,"value":1819},"docs.github.com\u002Fen\u002Factions",{"type":504,"tag":543,"props":1821,"children":1822},{},[1823,1828,1829],{"type":504,"tag":520,"props":1824,"children":1825},{},[1826],{"type":510,"value":1827},"Turborepo 官方文档",{"type":510,"value":729},{"type":504,"tag":1812,"props":1830,"children":1833},{"href":1831,"rel":1832},"https:\u002F\u002Fturbo.build\u002Frepo\u002Fdocs\u002Fci",[1816],[1834],{"type":510,"value":1835},"CI with Turborepo",{"type":504,"tag":543,"props":1837,"children":1838},{},[1839,1844,1845],{"type":504,"tag":520,"props":1840,"children":1841},{},[1842],{"type":510,"value":1843},"pnpm in CI",{"type":510,"value":729},{"type":504,"tag":1812,"props":1846,"children":1849},{"href":1847,"rel":1848},"https:\u002F\u002Fpnpm.io\u002Fcontinuous-integration",[1816],[1850],{"type":510,"value":1851},"pnpm.io\u002Fcontinuous-integration",{"type":504,"tag":1853,"props":1854,"children":1855},"style",{},[1856],{"type":510,"value":1857},"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":742,"searchDepth":772,"depth":772,"links":1859},[1860,1861,1862,1867,1868],{"id":534,"depth":772,"text":537},{"id":612,"depth":772,"text":615},{"id":695,"depth":772,"text":698,"children":1863},[1864,1865,1866],{"id":716,"depth":782,"text":719},{"id":1495,"depth":782,"text":1498},{"id":1607,"depth":782,"text":1610},{"id":1718,"depth":772,"text":1721},{"id":1795,"depth":772,"text":1798},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1870},"\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":1872},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1874},"\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":1876,"height":1876,"rotate":4,"vFlip":6,"hFlip":6,"body":1877},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>"]