[{"data":1,"prerenderedAt":2989},["ShallowReactive",2],{"article-/topics/frontend/frontend-automation-testing-strategy":3,"related-frontend":611,"content-query-lmDarmCagp":2527},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":12,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":23,"_type":605,"_id":606,"_source":607,"_file":608,"_stem":609,"_extension":610},"/topics/frontend/frontend-automation-testing-strategy","frontend",false,"","前端自动化测试策略：不是堆测试数量，而是设计反馈回路","从测试金字塔、单元测试、集成测试到 E2E 回归，系统讲清前端团队如何设计自动化测试分层、控制维护成本，并把测试真正接入发布流程，而不是停留在“写了很多但没人信”。","2026-04-08","HTMLPAGE 团队",[13,14,15,16,17],"Testing","Frontend","Vitest","Playwright","CI","/images/topics/frontend/frontend-automation-testing-strategy.jpg","software testing qa engineer laptop office",3862599,"https://www.pexels.com/photo/female-engineer-using-laptop-3862599/",16,{"type":24,"children":25,"toc":591},"root",[26,34,40,45,49,55,60,165,170,173,179,184,204,209,214,217,223,228,251,264,269,272,278,283,288,311,316,319,325,330,353,358,363,366,372,377,396,401,406,429,432,438,443,466,471,494,499,502,508,541,544,550,555,560],{"type":27,"tag":28,"props":29,"children":31},"element","h2",{"id":30},"前端自动化测试策略不是堆测试数量而是设计反馈回路",[32],{"type":33,"value":8},"text",{"type":27,"tag":35,"props":36,"children":37},"p",{},[38],{"type":33,"value":39},"很多团队的测试问题，不是“没有测试”，而是测试体系无法提供可靠反馈。有人写了大量单元测试，却挡不住线上回归；有人迷信 E2E，全链路一跑就是二十分钟，最终发布前只能手动跳过。问题不在工具，而在策略。",{"type":27,"tag":35,"props":41,"children":42},{},[43],{"type":33,"value":44},"自动化测试的目标不是追求覆盖率数字，而是让不同风险在不同成本下被尽早发现。",{"type":27,"tag":46,"props":47,"children":48},"hr",{},[],{"type":27,"tag":28,"props":50,"children":52},{"id":51},"_1-先回答你到底想防什么风险",[53],{"type":33,"value":54},"1. 先回答：你到底想防什么风险",{"type":27,"tag":35,"props":56,"children":57},{},[58],{"type":33,"value":59},"前端自动化测试常见要覆盖四类风险：",{"type":27,"tag":61,"props":62,"children":63},"table",{},[64,88],{"type":27,"tag":65,"props":66,"children":67},"thead",{},[68],{"type":27,"tag":69,"props":70,"children":71},"tr",{},[72,78,83],{"type":27,"tag":73,"props":74,"children":75},"th",{},[76],{"type":33,"value":77},"风险",{"type":27,"tag":73,"props":79,"children":80},{},[81],{"type":33,"value":82},"最适合的手段",{"type":27,"tag":73,"props":84,"children":85},{},[86],{"type":33,"value":87},"说明",{"type":27,"tag":89,"props":90,"children":91},"tbody",{},[92,111,129,147],{"type":27,"tag":69,"props":93,"children":94},{},[95,101,106],{"type":27,"tag":96,"props":97,"children":98},"td",{},[99],{"type":33,"value":100},"纯函数或状态转换错误",{"type":27,"tag":96,"props":102,"children":103},{},[104],{"type":33,"value":105},"单元测试",{"type":27,"tag":96,"props":107,"children":108},{},[109],{"type":33,"value":110},"快、稳定、定位准",{"type":27,"tag":69,"props":112,"children":113},{},[114,119,124],{"type":27,"tag":96,"props":115,"children":116},{},[117],{"type":33,"value":118},"组件交互失效",{"type":27,"tag":96,"props":120,"children":121},{},[122],{"type":33,"value":123},"组件/集成测试",{"type":27,"tag":96,"props":125,"children":126},{},[127],{"type":33,"value":128},"能覆盖事件、渲染、状态联动",{"type":27,"tag":69,"props":130,"children":131},{},[132,137,142],{"type":27,"tag":96,"props":133,"children":134},{},[135],{"type":33,"value":136},"页面流程断裂",{"type":27,"tag":96,"props":138,"children":139},{},[140],{"type":33,"value":141},"E2E 测试",{"type":27,"tag":96,"props":143,"children":144},{},[145],{"type":33,"value":146},"验证真实用户路径",{"type":27,"tag":69,"props":148,"children":149},{},[150,155,160],{"type":27,"tag":96,"props":151,"children":152},{},[153],{"type":33,"value":154},"构建、类型、规范回归",{"type":27,"tag":96,"props":156,"children":157},{},[158],{"type":33,"value":159},"静态检查 + CI 门禁",{"type":27,"tag":96,"props":161,"children":162},{},[163],{"type":33,"value":164},"在更早阶段拦截",{"type":27,"tag":35,"props":166,"children":167},{},[168],{"type":33,"value":169},"一上来就问“该写多少 Playwright”通常没有意义。先看你最贵的线上事故来自哪里。",{"type":27,"tag":46,"props":171,"children":172},{},[],{"type":27,"tag":28,"props":174,"children":176},{"id":175},"_2-测试金字塔仍然有效但要按前端现实调整",[177],{"type":33,"value":178},"2. 测试金字塔仍然有效，但要按前端现实调整",{"type":27,"tag":35,"props":180,"children":181},{},[182],{"type":33,"value":183},"经典模型仍适用：",{"type":27,"tag":185,"props":186,"children":187},"ul",{},[188,194,199],{"type":27,"tag":189,"props":190,"children":191},"li",{},[192],{"type":33,"value":193},"底层：静态检查、类型检查、少量纯单元测试",{"type":27,"tag":189,"props":195,"children":196},{},[197],{"type":33,"value":198},"中层：组件/集成测试",{"type":27,"tag":189,"props":200,"children":201},{},[202],{"type":33,"value":203},"顶层：少量关键路径 E2E",{"type":27,"tag":35,"props":205,"children":206},{},[207],{"type":33,"value":208},"但现代前端的现实是：组件复杂度越来越高，很多真实问题出在“组件状态 + 数据流 + DOM 交互”的交界处。所以中层测试往往比过去更重要。",{"type":27,"tag":35,"props":210,"children":211},{},[212],{"type":33,"value":213},"换句话说，别把全部预算压在最顶层，也别以为只测工具函数就叫体系完整。",{"type":27,"tag":46,"props":215,"children":216},{},[],{"type":27,"tag":28,"props":218,"children":220},{"id":219},"_3-单元测试适合规则不适合整个世界",[221],{"type":33,"value":222},"3. 单元测试适合“规则”，不适合“整个世界”",{"type":27,"tag":35,"props":224,"children":225},{},[226],{"type":33,"value":227},"单元测试最适合验证可预测的规则：",{"type":27,"tag":185,"props":229,"children":230},{},[231,236,241,246],{"type":27,"tag":189,"props":232,"children":233},{},[234],{"type":33,"value":235},"格式化逻辑",{"type":27,"tag":189,"props":237,"children":238},{},[239],{"type":33,"value":240},"校验规则",{"type":27,"tag":189,"props":242,"children":243},{},[244],{"type":33,"value":245},"状态机转移",{"type":27,"tag":189,"props":247,"children":248},{},[249],{"type":33,"value":250},"价格、权限、过滤器等纯计算",{"type":27,"tag":252,"props":253,"children":258},"pre",{"className":254,"code":256,"language":257,"meta":7},[255],"language-ts","import { describe, expect, it } from 'vitest'\nimport { normalizePromptInput } from './normalizePromptInput'\n\ndescribe('normalizePromptInput', () => {\n  it('removes duplicated blank lines and trims edges', () => {\n    expect(normalizePromptInput('  hello\\n\\n\\nworld  ')).toBe('hello\\n\\nworld')\n  })\n})\n","ts",[259],{"type":27,"tag":260,"props":261,"children":262},"code",{"__ignoreMap":7},[263],{"type":33,"value":256},{"type":27,"tag":35,"props":265,"children":266},{},[267],{"type":33,"value":268},"它不适合承担整页交互的主要信心来源。若你必须 mock 三层依赖、十几个 hooks 才能测一个函数，说明边界设计已经出了问题。",{"type":27,"tag":46,"props":270,"children":271},{},[],{"type":27,"tag":28,"props":273,"children":275},{"id":274},"_4-集成测试是前端自动化的主战场",[276],{"type":33,"value":277},"4. 集成测试是前端自动化的主战场",{"type":27,"tag":35,"props":279,"children":280},{},[281],{"type":33,"value":282},"对大多数业务站点来说，最有性价比的是组件或页面级集成测试。原因很简单：用户的问题往往发生在“点一下以后发生了什么”，而不是某个 util 单独算错一位数。",{"type":27,"tag":35,"props":284,"children":285},{},[286],{"type":33,"value":287},"例如：",{"type":27,"tag":185,"props":289,"children":290},{},[291,296,301,306],{"type":27,"tag":189,"props":292,"children":293},{},[294],{"type":33,"value":295},"表单验证是否正确提示",{"type":27,"tag":189,"props":297,"children":298},{},[299],{"type":33,"value":300},"请求失败时按钮是否解除 loading",{"type":27,"tag":189,"props":302,"children":303},{},[304],{"type":33,"value":305},"搜索条件变化是否触发正确渲染",{"type":27,"tag":189,"props":307,"children":308},{},[309],{"type":33,"value":310},"对话面板在上下文为空时是否展示 fallback",{"type":27,"tag":35,"props":312,"children":313},{},[314],{"type":33,"value":315},"这类测试既比 E2E 稳定，又比纯单元测试更贴近真实行为。",{"type":27,"tag":46,"props":317,"children":318},{},[],{"type":27,"tag":28,"props":320,"children":322},{"id":321},"_5-e2e-只测关键路径不要试图替代全部测试",[323],{"type":33,"value":324},"5. E2E 只测关键路径，不要试图替代全部测试",{"type":27,"tag":35,"props":326,"children":327},{},[328],{"type":33,"value":329},"E2E 最适合做发布回归的“最后一道保险”，而不是覆盖所有细枝末节。推荐聚焦：",{"type":27,"tag":185,"props":331,"children":332},{},[333,338,343,348],{"type":27,"tag":189,"props":334,"children":335},{},[336],{"type":33,"value":337},"登录与身份恢复",{"type":27,"tag":189,"props":339,"children":340},{},[341],{"type":33,"value":342},"购买/提交/保存等关键转化路径",{"type":27,"tag":189,"props":344,"children":345},{},[346],{"type":33,"value":347},"内容发布、权限切换、核心 CRUD",{"type":27,"tag":189,"props":349,"children":350},{},[351],{"type":33,"value":352},"过去最常出事故的真实链路",{"type":27,"tag":35,"props":354,"children":355},{},[356],{"type":33,"value":357},"一个常见错误是：把每个表单边界、每个提示文案都写成 E2E。结果脚本数量越来越多，波动越来越大，最终团队开始怀疑测试结果本身。",{"type":27,"tag":35,"props":359,"children":360},{},[361],{"type":33,"value":362},"少而准的 E2E，比泛滥的 E2E 更能守住发布节奏。",{"type":27,"tag":46,"props":364,"children":365},{},[],{"type":27,"tag":28,"props":367,"children":369},{"id":368},"_6-失败案例追求覆盖率最后没人愿意改代码",[370],{"type":33,"value":371},"6. 失败案例：追求覆盖率，最后没人愿意改代码",{"type":27,"tag":35,"props":373,"children":374},{},[375],{"type":33,"value":376},"有些团队会把测试 KPI 绑定到覆盖率，结果是：",{"type":27,"tag":378,"props":379,"children":380},"ol",{},[381,386,391],{"type":27,"tag":189,"props":382,"children":383},{},[384],{"type":33,"value":385},"写大量低价值测试满足数字",{"type":27,"tag":189,"props":387,"children":388},{},[389],{"type":33,"value":390},"UI 文案一改，测试成片失败",{"type":27,"tag":189,"props":392,"children":393},{},[394],{"type":33,"value":395},"开发开始为了“别动测试”而回避重构",{"type":27,"tag":35,"props":397,"children":398},{},[399],{"type":33,"value":400},"测试本来应该保护重构，最后却阻碍重构，这就是策略失真。",{"type":27,"tag":35,"props":402,"children":403},{},[404],{"type":33,"value":405},"更合理的目标是：",{"type":27,"tag":185,"props":407,"children":408},{},[409,414,419,424],{"type":27,"tag":189,"props":410,"children":411},{},[412],{"type":33,"value":413},"核心流程可回归",{"type":27,"tag":189,"props":415,"children":416},{},[417],{"type":33,"value":418},"高频事故点有防线",{"type":27,"tag":189,"props":420,"children":421},{},[422],{"type":33,"value":423},"失败后能快速定位原因",{"type":27,"tag":189,"props":425,"children":426},{},[427],{"type":33,"value":428},"测试成本低于它防住的事故成本",{"type":27,"tag":46,"props":430,"children":431},{},[],{"type":27,"tag":28,"props":433,"children":435},{"id":434},"_7-推荐的最小落地组合",[436],{"type":33,"value":437},"7. 推荐的最小落地组合",{"type":27,"tag":35,"props":439,"children":440},{},[441],{"type":33,"value":442},"对多数前端团队，一个比较平衡的组合是：",{"type":27,"tag":185,"props":444,"children":445},{},[446,451,456,461],{"type":27,"tag":189,"props":447,"children":448},{},[449],{"type":33,"value":450},"ESLint + TypeScript：拦语义与类型问题",{"type":27,"tag":189,"props":452,"children":453},{},[454],{"type":33,"value":455},"Vitest：测工具函数、状态逻辑、部分组件集成",{"type":27,"tag":189,"props":457,"children":458},{},[459],{"type":33,"value":460},"Playwright：测关键路径 E2E",{"type":27,"tag":189,"props":462,"children":463},{},[464],{"type":33,"value":465},"CI：按层级拆执行时机",{"type":27,"tag":35,"props":467,"children":468},{},[469],{"type":33,"value":470},"一个可执行的流水线通常是：",{"type":27,"tag":378,"props":472,"children":473},{},[474,479,484,489],{"type":27,"tag":189,"props":475,"children":476},{},[477],{"type":33,"value":478},"提交前跑 lint + typecheck",{"type":27,"tag":189,"props":480,"children":481},{},[482],{"type":33,"value":483},"PR 跑单元与集成测试",{"type":27,"tag":189,"props":485,"children":486},{},[487],{"type":33,"value":488},"合并前或 nightly 跑核心 E2E",{"type":27,"tag":189,"props":490,"children":491},{},[492],{"type":33,"value":493},"生产前对关键转化流程做冒烟回归",{"type":27,"tag":35,"props":495,"children":496},{},[497],{"type":33,"value":498},"这样反馈速度与信心不会彼此冲突。",{"type":27,"tag":46,"props":500,"children":501},{},[],{"type":27,"tag":28,"props":503,"children":505},{"id":504},"_8-checklist判断你的测试体系是否在健康工作",[506],{"type":33,"value":507},"8. Checklist：判断你的测试体系是否在健康工作",{"type":27,"tag":185,"props":509,"children":510},{},[511,516,521,526,531,536],{"type":27,"tag":189,"props":512,"children":513},{},[514],{"type":33,"value":515},"团队是否说得清每一层测试负责什么风险",{"type":27,"tag":189,"props":517,"children":518},{},[519],{"type":33,"value":520},"失败时是否能快速定位到具体模块而非整页黑盒",{"type":27,"tag":189,"props":522,"children":523},{},[524],{"type":33,"value":525},"E2E 是否只覆盖关键业务路径",{"type":27,"tag":189,"props":527,"children":528},{},[529],{"type":33,"value":530},"UI 文案微调是否不会触发大面积误报",{"type":27,"tag":189,"props":532,"children":533},{},[534],{"type":33,"value":535},"发布流程是否真的依赖测试结果，而不是把它当摆设",{"type":27,"tag":189,"props":537,"children":538},{},[539],{"type":33,"value":540},"历史线上事故是否被转化成新的回归用例",{"type":27,"tag":46,"props":542,"children":543},{},[],{"type":27,"tag":28,"props":545,"children":547},{"id":546},"_9-结论自动化测试的核心是反馈设计不是工具清单",[548],{"type":33,"value":549},"9. 结论：自动化测试的核心是反馈设计，不是工具清单",{"type":27,"tag":35,"props":551,"children":552},{},[553],{"type":33,"value":554},"前端测试策略真正决定成败的，不是用了 Vitest 还是 Playwright，而是团队有没有把风险按层级拆开，把不同成本的检查放在合适的位置。测试体系一旦变成稳定反馈回路，它就不再是“额外负担”，而是让你敢于迭代的底层保障。",{"type":27,"tag":35,"props":556,"children":557},{},[558],{"type":33,"value":559},"推荐继续阅读：",{"type":27,"tag":185,"props":561,"children":562},{},[563,573,582],{"type":27,"tag":189,"props":564,"children":565},{},[566],{"type":27,"tag":567,"props":568,"children":570},"a",{"href":569},"/topics/frontend/unit-testing-best-practices",[571],{"type":33,"value":572},"单元测试最佳实践",{"type":27,"tag":189,"props":574,"children":575},{},[576],{"type":27,"tag":567,"props":577,"children":579},{"href":578},"/topics/frontend/e2e-testing-comparison",[580],{"type":33,"value":581},"E2E 测试框架对比",{"type":27,"tag":189,"props":583,"children":584},{},[585],{"type":27,"tag":567,"props":586,"children":588},{"href":587},"/topics/frontend/code-quality-gates-ci-integration",[589],{"type":33,"value":590},"代码质量门禁与 CI 集成",{"title":7,"searchDepth":592,"depth":592,"links":593},3,[594,596,597,598,599,600,601,602,603,604],{"id":30,"depth":595,"text":8},2,{"id":51,"depth":595,"text":54},{"id":175,"depth":595,"text":178},{"id":219,"depth":595,"text":222},{"id":274,"depth":595,"text":277},{"id":321,"depth":595,"text":324},{"id":368,"depth":595,"text":371},{"id":434,"depth":595,"text":437},{"id":504,"depth":595,"text":507},{"id":546,"depth":595,"text":549},"markdown","content:topics:frontend:frontend-automation-testing-strategy.md","content","topics/frontend/frontend-automation-testing-strategy.md","topics/frontend/frontend-automation-testing-strategy","md",[612,944,1256],{"_path":613,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":614,"description":615,"keywords":616,"image":622,"author":623,"date":624,"readingTime":625,"topic":5,"body":626,"_type":605,"_id":941,"_source":607,"_file":942,"_stem":943,"_extension":610},"/topics/frontend/react-hooks-guide","React Hooks 完全指南","全面讲解 React Hooks，包括内置钩子、自定义钩子和最佳实践",[617,618,619,620,621],"React","Hooks","自定义钩子","状态管理","函数组件","/images/topics/react-hooks-guide.jpg","AI Content Team","2025-12-08",23,{"type":24,"children":627,"toc":922},[628,633,638,644,651,662,668,677,683,692,698,704,713,719,728,734,743,749,758,763,769,778,783,796,824,835,863,868],{"type":27,"tag":28,"props":629,"children":631},{"id":630},"react-hooks-完全指南",[632],{"type":33,"value":614},{"type":27,"tag":35,"props":634,"children":635},{},[636],{"type":33,"value":637},"Hooks 改变了 React 的开发方式。本文全面讲解如何使用和创建 Hooks。",{"type":27,"tag":28,"props":639,"children":641},{"id":640},"内置-hooks",[642],{"type":33,"value":643},"内置 Hooks",{"type":27,"tag":645,"props":646,"children":648},"h3",{"id":647},"usestate-状态管理",[649],{"type":33,"value":650},"useState - 状态管理",{"type":27,"tag":252,"props":652,"children":657},{"className":653,"code":655,"language":656,"meta":7},[654],"language-javascript","import { useState } from 'react'\n\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  const [name, setName] = useState('John')\n  const [user, setUser] = useState({\n    age: 30,\n    email: 'john@example.com',\n  })\n  \n  // 使用函数初始化状态（对于复杂初始值）\n  const [data, setData] = useState(() => {\n    console.log('初始化数据...')\n    return fetchInitialData() // 仅在首次渲染时调用\n  })\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>计数: {count}\u003C/p>\n      \u003Cbutton onClick={() => setCount(count + 1)}>增加\u003C/button>\n      \n      {/* 函数式更新 */}\n      \u003Cbutton onClick={() => setCount(prev => prev + 1)}>\n        函数式增加\n      \u003C/button>\n      \n      {/* 更新对象 */}\n      \u003Cbutton onClick={() => setUser({ ...user, age: user.age + 1 })}>\n        增加年龄\n      \u003C/button>\n    \u003C/div>\n  )\n}\n","javascript",[658],{"type":27,"tag":260,"props":659,"children":660},{"__ignoreMap":7},[661],{"type":33,"value":655},{"type":27,"tag":645,"props":663,"children":665},{"id":664},"useeffect-副作用处理",[666],{"type":33,"value":667},"useEffect - 副作用处理",{"type":27,"tag":252,"props":669,"children":672},{"className":670,"code":671,"language":656,"meta":7},[654],"import { useState, useEffect } from 'react'\n\nfunction DataFetcher() {\n  const [data, setData] = useState(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState(null)\n  const [userId, setUserId] = useState(1)\n  \n  // 副作用 - 每次渲染后执行\n  useEffect(() => {\n    console.log('组件已挂载或已更新')\n  })\n  \n  // 挂载时执行一次\n  useEffect(() => {\n    console.log('组件已挂载')\n    \n    return () => {\n      console.log('组件已卸载')\n    }\n  }, [])\n  \n  // 当 userId 改变时执行\n  useEffect(() => {\n    let isMounted = true // 防止内存泄漏\n    \n    const fetchData = async () => {\n      setLoading(true)\n      try {\n        const response = await fetch(\\`/api/users/\\${userId}\\`)\n        const result = await response.json()\n        \n        if (isMounted) {\n          setData(result)\n        }\n      } catch (err) {\n        if (isMounted) {\n          setError(err)\n        }\n      } finally {\n        if (isMounted) {\n          setLoading(false)\n        }\n      }\n    }\n    \n    fetchData()\n    \n    // 清理函数\n    return () => {\n      isMounted = false\n    }\n  }, [userId])\n  \n  if (loading) return \u003Cp>加载中...\u003C/p>\n  if (error) return \u003Cp>错误: {error.message}\u003C/p>\n  \n  return \u003Cdiv>{data && JSON.stringify(data)}\u003C/div>\n}\n",[673],{"type":27,"tag":260,"props":674,"children":675},{"__ignoreMap":7},[676],{"type":33,"value":671},{"type":27,"tag":645,"props":678,"children":680},{"id":679},"usecontext-跨组件通信",[681],{"type":33,"value":682},"useContext - 跨组件通信",{"type":27,"tag":252,"props":684,"children":687},{"className":685,"code":686,"language":656,"meta":7},[654],"import { createContext, useContext, useState } from 'react'\n\n// 创建上下文\nconst ThemeContext = createContext()\n\n// 提供者组件\nfunction ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('light')\n  \n  const toggleTheme = () => {\n    setTheme(prev => prev === 'light' ? 'dark' : 'light')\n  }\n  \n  const value = { theme, toggleTheme }\n  \n  return (\n    \u003CThemeContext.Provider value={value}>\n      {children}\n    \u003C/ThemeContext.Provider>\n  )\n}\n\n// 使用 Hook\nfunction useTheme() {\n  const context = useContext(ThemeContext)\n  \n  if (!context) {\n    throw new Error('useTheme 必须在 ThemeProvider 内使用')\n  }\n  \n  return context\n}\n\n// 组件使用\nfunction App() {\n  const { theme, toggleTheme } = useTheme()\n  \n  return (\n    \u003Cdiv style={{\n      background: theme === 'light' ? '#fff' : '#333',\n      color: theme === 'light' ? '#000' : '#fff',\n    }}>\n      \u003Cp>当前主题: {theme}\u003C/p>\n      \u003Cbutton onClick={toggleTheme}>切换主题\u003C/button>\n    \u003C/div>\n  )\n}\n\n// 使用\nexport default function Root() {\n  return (\n    \u003CThemeProvider>\n      \u003CApp />\n    \u003C/ThemeProvider>\n  )\n}\n",[688],{"type":27,"tag":260,"props":689,"children":690},{"__ignoreMap":7},[691],{"type":33,"value":686},{"type":27,"tag":28,"props":693,"children":695},{"id":694},"自定义-hooks",[696],{"type":33,"value":697},"自定义 Hooks",{"type":27,"tag":645,"props":699,"children":701},{"id":700},"uselocalstorage",[702],{"type":33,"value":703},"useLocalStorage",{"type":27,"tag":252,"props":705,"children":708},{"className":706,"code":707,"language":656,"meta":7},[654],"import { useState, useEffect } from 'react'\n\nfunction useLocalStorage(key, initialValue) {\n  // 从本地存储获取初始值\n  const [storedValue, setStoredValue] = useState(() => {\n    try {\n      const item = window.localStorage.getItem(key)\n      return item ? JSON.parse(item) : initialValue\n    } catch (error) {\n      console.error(error)\n      return initialValue\n    }\n  })\n  \n  // 当值改变时更新本地存储\n  const setValue = (value) => {\n    try {\n      const valueToStore = value instanceof Function ? value(storedValue) : value\n      setStoredValue(valueToStore)\n      window.localStorage.setItem(key, JSON.stringify(valueToStore))\n    } catch (error) {\n      console.error(error)\n    }\n  }\n  \n  return [storedValue, setValue]\n}\n\n// 使用\nfunction App() {\n  const [name, setName] = useLocalStorage('name', 'Guest')\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>姓名: {name}\u003C/p>\n      \u003Cinput\n        value={name}\n        onChange={(e) => setName(e.target.value)}\n      />\n    \u003C/div>\n  )\n}\n",[709],{"type":27,"tag":260,"props":710,"children":711},{"__ignoreMap":7},[712],{"type":33,"value":707},{"type":27,"tag":645,"props":714,"children":716},{"id":715},"useasync-异步操作",[717],{"type":33,"value":718},"useAsync - 异步操作",{"type":27,"tag":252,"props":720,"children":723},{"className":721,"code":722,"language":656,"meta":7},[654],"import { useState, useEffect, useRef } from 'react'\n\nfunction useAsync(asyncFunction, immediate = true) {\n  const [status, setStatus] = useState('idle')\n  const [value, setValue] = useState(null)\n  const [error, setError] = useState(null)\n  \n  // 使用 ref 来防止无限循环\n  const executeRef = useRef(null)\n  \n  const execute = useRef(async () => {\n    setStatus('pending')\n    setValue(null)\n    setError(null)\n    \n    try {\n      const response = await asyncFunction()\n      setValue(response)\n      setStatus('success')\n      return response\n    } catch (error) {\n      setError(error)\n      setStatus('error')\n    }\n  })\n  \n  executeRef.current = execute.current\n  \n  useEffect(() => {\n    if (!immediate) return\n    \n    executeRef.current()\n  }, [immediate])\n  \n  return { execute: executeRef.current, status, value, error }\n}\n\n// 使用\nfunction UserProfile({ userId }) {\n  const { execute, status, value: user, error } = useAsync(\n    () => fetch(\\`/api/users/\\${userId}\\`).then(r => r.json()),\n    true\n  )\n  \n  if (status === 'pending') return \u003Cp>加载中...\u003C/p>\n  if (status === 'error') return \u003Cp>错误: {error?.message}\u003C/p>\n  if (status === 'success') return \u003Cp>用户: {user?.name}\u003C/p>\n  \n  return null\n}\n",[724],{"type":27,"tag":260,"props":725,"children":726},{"__ignoreMap":7},[727],{"type":33,"value":722},{"type":27,"tag":645,"props":729,"children":731},{"id":730},"usefetch-数据获取",[732],{"type":33,"value":733},"useFetch - 数据获取",{"type":27,"tag":252,"props":735,"children":738},{"className":736,"code":737,"language":656,"meta":7},[654],"import { useState, useEffect } from 'react'\n\nfunction useFetch(url, options = {}) {\n  const [data, setData] = useState(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState(null)\n  \n  useEffect(() => {\n    let isMounted = true\n    \n    const fetchData = async () => {\n      try {\n        const response = await fetch(url, {\n          method: 'GET',\n          ...options,\n        })\n        \n        if (!response.ok) {\n          throw new Error(\\`HTTP error! status: \\${response.status}\\`)\n        }\n        \n        const result = await response.json()\n        \n        if (isMounted) {\n          setData(result)\n          setError(null)\n        }\n      } catch (err) {\n        if (isMounted) {\n          setError(err)\n          setData(null)\n        }\n      } finally {\n        if (isMounted) {\n          setLoading(false)\n        }\n      }\n    }\n    \n    fetchData()\n    \n    return () => {\n      isMounted = false\n    }\n  }, [url, options])\n  \n  const refetch = async () => {\n    setLoading(true)\n    try {\n      const response = await fetch(url, options)\n      const result = await response.json()\n      setData(result)\n    } catch (err) {\n      setError(err)\n    } finally {\n      setLoading(false)\n    }\n  }\n  \n  return { data, loading, error, refetch }\n}\n\n// 使用\nfunction UserList() {\n  const { data: users, loading, error, refetch } = useFetch('/api/users')\n  \n  if (loading) return \u003Cp>加载中...\u003C/p>\n  if (error) return \u003Cp>错误: {error.message}\u003C/p>\n  \n  return (\n    \u003Cdiv>\n      \u003Cbutton onClick={refetch}>刷新\u003C/button>\n      \u003Cul>\n        {users?.map(user => (\n          \u003Cli key={user.id}>{user.name}\u003C/li>\n        ))}\n      \u003C/ul>\n    \u003C/div>\n  )\n}\n",[739],{"type":27,"tag":260,"props":740,"children":741},{"__ignoreMap":7},[742],{"type":33,"value":737},{"type":27,"tag":645,"props":744,"children":746},{"id":745},"useprevious-保存前一个值",[747],{"type":33,"value":748},"usePrevious - 保存前一个值",{"type":27,"tag":252,"props":750,"children":753},{"className":751,"code":752,"language":656,"meta":7},[654],"import { useEffect, useRef } from 'react'\n\nfunction usePrevious(value) {\n  const ref = useRef()\n  \n  useEffect(() => {\n    ref.current = value\n  }, [value])\n  \n  return ref.current\n}\n\n// 使用\nfunction Counter() {\n  const [count, setCount] = React.useState(0)\n  const prevCount = usePrevious(count)\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>当前: {count}, 前一个: {prevCount}\u003C/p>\n      \u003Cbutton onClick={() => setCount(count + 1)}>增加\u003C/button>\n    \u003C/div>\n  )\n}\n",[754],{"type":27,"tag":260,"props":755,"children":756},{"__ignoreMap":7},[757],{"type":33,"value":752},{"type":27,"tag":28,"props":759,"children":761},{"id":760},"高级模式",[762],{"type":33,"value":760},{"type":27,"tag":645,"props":764,"children":766},{"id":765},"usereducer-复杂状态管理",[767],{"type":33,"value":768},"useReducer - 复杂状态管理",{"type":27,"tag":252,"props":770,"children":773},{"className":771,"code":772,"language":656,"meta":7},[654],"import { useReducer } from 'react'\n\nconst initialState = {\n  todos: [],\n  filter: 'all',\n  error: null,\n}\n\nfunction todoReducer(state, action) {\n  switch (action.type) {\n    case 'ADD_TODO':\n      return {\n        ...state,\n        todos: [...state.todos, { id: Date.now(), text: action.payload }],\n      }\n    \n    case 'REMOVE_TODO':\n      return {\n        ...state,\n        todos: state.todos.filter(todo => todo.id !== action.payload),\n      }\n    \n    case 'SET_FILTER':\n      return { ...state, filter: action.payload }\n    \n    case 'SET_ERROR':\n      return { ...state, error: action.payload }\n    \n    default:\n      return state\n  }\n}\n\nfunction TodoApp() {\n  const [state, dispatch] = useReducer(todoReducer, initialState)\n  \n  const addTodo = (text) => {\n    dispatch({ type: 'ADD_TODO', payload: text })\n  }\n  \n  const removeTodo = (id) => {\n    dispatch({ type: 'REMOVE_TODO', payload: id })\n  }\n  \n  return (\n    \u003Cdiv>\n      {state.todos.map(todo => (\n        \u003Cdiv key={todo.id}>\n          {todo.text}\n          \u003Cbutton onClick={() => removeTodo(todo.id)}>删除\u003C/button>\n        \u003C/div>\n      ))}\n    \u003C/div>\n  )\n}\n",[774],{"type":27,"tag":260,"props":775,"children":776},{"__ignoreMap":7},[777],{"type":33,"value":772},{"type":27,"tag":28,"props":779,"children":781},{"id":780},"最佳实践",[782],{"type":33,"value":780},{"type":27,"tag":35,"props":784,"children":785},{},[786,788,794],{"type":33,"value":787},"✅ ",{"type":27,"tag":789,"props":790,"children":791},"strong",{},[792],{"type":33,"value":793},"应该做的事",{"type":33,"value":795},":",{"type":27,"tag":185,"props":797,"children":798},{},[799,804,809,814,819],{"type":27,"tag":189,"props":800,"children":801},{},[802],{"type":33,"value":803},"将相关逻辑提取到自定义 Hooks",{"type":27,"tag":189,"props":805,"children":806},{},[807],{"type":33,"value":808},"在 useEffect 的依赖数组中包含所有依赖",{"type":27,"tag":189,"props":810,"children":811},{},[812],{"type":33,"value":813},"使用 useCallback 和 useMemo 优化性能",{"type":27,"tag":189,"props":815,"children":816},{},[817],{"type":33,"value":818},"为自定义 Hooks 编写文档",{"type":27,"tag":189,"props":820,"children":821},{},[822],{"type":33,"value":823},"及时清理副作用",{"type":27,"tag":35,"props":825,"children":826},{},[827,829,834],{"type":33,"value":828},"❌ ",{"type":27,"tag":789,"props":830,"children":831},{},[832],{"type":33,"value":833},"不应该做的事",{"type":33,"value":795},{"type":27,"tag":185,"props":836,"children":837},{},[838,843,848,853,858],{"type":27,"tag":189,"props":839,"children":840},{},[841],{"type":33,"value":842},"在条件或循环中调用 Hooks",{"type":27,"tag":189,"props":844,"children":845},{},[846],{"type":33,"value":847},"在普通函数中调用 Hooks",{"type":27,"tag":189,"props":849,"children":850},{},[851],{"type":33,"value":852},"忘记依赖数组",{"type":27,"tag":189,"props":854,"children":855},{},[856],{"type":33,"value":857},"过度使用 useMemo/useCallback",{"type":27,"tag":189,"props":859,"children":860},{},[861],{"type":33,"value":862},"在 Hooks 中创建过多的闭包",{"type":27,"tag":28,"props":864,"children":866},{"id":865},"检查清单",[867],{"type":33,"value":865},{"type":27,"tag":185,"props":869,"children":872},{"className":870},[871],"contains-task-list",[873,886,895,904,913],{"type":27,"tag":189,"props":874,"children":877},{"className":875},[876],"task-list-item",[878,884],{"type":27,"tag":879,"props":880,"children":883},"input",{"disabled":881,"type":882},true,"checkbox",[],{"type":33,"value":885}," Hooks 调用顺序正确",{"type":27,"tag":189,"props":887,"children":889},{"className":888},[876],[890,893],{"type":27,"tag":879,"props":891,"children":892},{"disabled":881,"type":882},[],{"type":33,"value":894}," 依赖数组完整",{"type":27,"tag":189,"props":896,"children":898},{"className":897},[876],[899,902],{"type":27,"tag":879,"props":900,"children":901},{"disabled":881,"type":882},[],{"type":33,"value":903}," 副作用正确清理",{"type":27,"tag":189,"props":905,"children":907},{"className":906},[876],[908,911],{"type":27,"tag":879,"props":909,"children":910},{"disabled":881,"type":882},[],{"type":33,"value":912}," 性能优化得当",{"type":27,"tag":189,"props":914,"children":916},{"className":915},[876],[917,920],{"type":27,"tag":879,"props":918,"children":919},{"disabled":881,"type":882},[],{"type":33,"value":921}," 代码易于理解和测试",{"title":7,"searchDepth":592,"depth":592,"links":923},[924,925,930,936,939,940],{"id":630,"depth":595,"text":614},{"id":640,"depth":595,"text":643,"children":926},[927,928,929],{"id":647,"depth":592,"text":650},{"id":664,"depth":592,"text":667},{"id":679,"depth":592,"text":682},{"id":694,"depth":595,"text":697,"children":931},[932,933,934,935],{"id":700,"depth":592,"text":703},{"id":715,"depth":592,"text":718},{"id":730,"depth":592,"text":733},{"id":745,"depth":592,"text":748},{"id":760,"depth":595,"text":760,"children":937},[938],{"id":765,"depth":592,"text":768},{"id":780,"depth":595,"text":780},{"id":865,"depth":595,"text":865},"content:topics:frontend:react-hooks-guide.md","topics/frontend/react-hooks-guide.md","topics/frontend/react-hooks-guide",{"_path":945,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":946,"description":947,"keywords":948,"image":954,"author":623,"date":624,"readingTime":955,"topic":5,"body":956,"_type":605,"_id":1253,"_source":607,"_file":1254,"_stem":1255,"_extension":610},"/topics/frontend/vue3-composition-api","Vue 3 Composition API 深度解析","全面讲解 Vue 3 Composition API 的用法、最佳实践和高级模式",[949,950,951,952,953],"Vue 3","Composition API","组合式函数","响应式系统","前端开发","/images/topics/vue3-composition-api.jpg",22,{"type":24,"children":957,"toc":1229},[958,963,968,973,979,988,993,1002,1006,1011,1020,1025,1031,1040,1045,1051,1060,1065,1070,1079,1084,1090,1099,1103,1112,1140,1149,1177,1181],{"type":27,"tag":28,"props":959,"children":961},{"id":960},"vue-3-composition-api-深度解析",[962],{"type":33,"value":946},{"type":27,"tag":35,"props":964,"children":965},{},[966],{"type":33,"value":967},"Composition API 让 Vue 应用更易于组织和重用逻辑。本文深入讲解这一核心特性。",{"type":27,"tag":28,"props":969,"children":971},{"id":970},"核心概念",[972],{"type":33,"value":970},{"type":27,"tag":645,"props":974,"children":976},{"id":975},"setup-函数",[977],{"type":33,"value":978},"setup 函数",{"type":27,"tag":252,"props":980,"children":983},{"className":981,"code":982,"language":656,"meta":7},[654],"import { ref, computed, watch } from 'vue'\n\nexport default {\n  props: ['initialCount'],\n  emits: ['count-changed'],\n  \n  setup(props, { emit, slots, expose }) {\n    // 创建响应式状态\n    const count = ref(props.initialCount)\n    const doubled = computed(() => count.value * 2)\n    \n    // 监听状态变化\n    watch(count, (newVal, oldVal) => {\n      console.log(`Count changed from ${oldVal} to ${newVal}`)\n      emit('count-changed', newVal)\n    })\n    \n    // 定义方法\n    const increment = () => count.value++\n    const decrement = () => count.value--\n    \n    // 返回模板需要的内容\n    return {\n      count,\n      doubled,\n      increment,\n      decrement,\n    }\n  },\n}\n",[984],{"type":27,"tag":260,"props":985,"children":986},{"__ignoreMap":7},[987],{"type":33,"value":982},{"type":27,"tag":645,"props":989,"children":991},{"id":990},"响应式基础",[992],{"type":33,"value":990},{"type":27,"tag":252,"props":994,"children":997},{"className":995,"code":996,"language":656,"meta":7},[654],"import { ref, reactive, readonly, isRef } from 'vue'\n\n// ref - 用于基本类型\nconst count = ref(0)\nconsole.log(count.value) // 0\ncount.value++\n\n// reactive - 用于对象\nconst state = reactive({\n  name: 'John',\n  age: 30,\n  address: {\n    city: 'Beijing',\n  },\n})\n\nstate.name = 'Jane' // 自动更新，无需 .value\n\n// readonly - 创建只读副本\nconst original = reactive({ count: 0 })\nconst copy = readonly(original)\n// copy.count++ // 错误：不能修改\n\n// isRef 检查\nconsole.log(isRef(count)) // true\nconsole.log(isRef(state)) // false\n",[998],{"type":27,"tag":260,"props":999,"children":1000},{"__ignoreMap":7},[1001],{"type":33,"value":996},{"type":27,"tag":28,"props":1003,"children":1004},{"id":951},[1005],{"type":33,"value":951},{"type":27,"tag":645,"props":1007,"children":1009},{"id":1008},"创建可重用逻辑",[1010],{"type":33,"value":1008},{"type":27,"tag":252,"props":1012,"children":1015},{"className":1013,"code":1014,"language":656,"meta":7},[654],"// useCounter.js - 组合式函数\nimport { ref, computed } from 'vue'\n\nexport function useCounter(initialValue = 0) {\n  const count = ref(initialValue)\n  const doubled = computed(() => count.value * 2)\n  \n  const increment = () => count.value++\n  const decrement = () => count.value--\n  const reset = () => count.value = initialValue\n  \n  return {\n    count,\n    doubled,\n    increment,\n    decrement,\n    reset,\n  }\n}\n\n// useFetch.js - 数据获取组合式函数\nimport { ref, onMounted } from 'vue'\n\nexport function useFetch(url) {\n  const data = ref(null)\n  const loading = ref(false)\n  const error = ref(null)\n  \n  const fetch = async () => {\n    loading.value = true\n    error.value = null\n    \n    try {\n      const response = await fetch(url)\n      data.value = await response.json()\n    } catch (e) {\n      error.value = e\n    } finally {\n      loading.value = false\n    }\n  }\n  \n  onMounted(fetch)\n  \n  return {\n    data,\n    loading,\n    error,\n    refetch: fetch,\n  }\n}\n\n// 使用\nexport default {\n  setup() {\n    const { count, doubled, increment } = useCounter(10)\n    const { data, loading, refetch } = useFetch('/api/data')\n    \n    return {\n      count,\n      doubled,\n      increment,\n      data,\n      loading,\n      refetch,\n    }\n  },\n}\n",[1016],{"type":27,"tag":260,"props":1017,"children":1018},{"__ignoreMap":7},[1019],{"type":33,"value":1014},{"type":27,"tag":28,"props":1021,"children":1023},{"id":1022},"生命周期钩子",[1024],{"type":33,"value":1022},{"type":27,"tag":645,"props":1026,"children":1028},{"id":1027},"composition-api-中的生命周期",[1029],{"type":33,"value":1030},"Composition API 中的生命周期",{"type":27,"tag":252,"props":1032,"children":1035},{"className":1033,"code":1034,"language":656,"meta":7},[654],"import {\n  onBeforeMount,\n  onMounted,\n  onBeforeUpdate,\n  onUpdated,\n  onBeforeUnmount,\n  onUnmounted,\n  onErrorCaptured,\n} from 'vue'\n\nexport default {\n  setup() {\n    onBeforeMount(() => {\n      console.log('组件挂载前')\n    })\n    \n    onMounted(() => {\n      console.log('组件已挂载')\n      // 初始化事件监听器、定时器等\n    })\n    \n    onBeforeUpdate(() => {\n      console.log('组件更新前')\n    })\n    \n    onUpdated(() => {\n      console.log('组件已更新')\n    })\n    \n    onBeforeUnmount(() => {\n      console.log('组件卸载前')\n    })\n    \n    onUnmounted(() => {\n      console.log('组件已卸载')\n      // 清理事件监听器、定时器等\n    })\n    \n    onErrorCaptured((err, instance, info) => {\n      console.log('捕获错误:', err)\n      return false // 返回 false 阻止错误传播\n    })\n    \n    return {}\n  },\n}\n",[1036],{"type":27,"tag":260,"props":1037,"children":1038},{"__ignoreMap":7},[1039],{"type":33,"value":1034},{"type":27,"tag":28,"props":1041,"children":1043},{"id":1042},"模板引用",[1044],{"type":33,"value":1042},{"type":27,"tag":645,"props":1046,"children":1048},{"id":1047},"访问-dom-元素",[1049],{"type":33,"value":1050},"访问 DOM 元素",{"type":27,"tag":252,"props":1052,"children":1055},{"className":1053,"code":1054,"language":656,"meta":7},[654],"import { ref, onMounted } from 'vue'\n\nexport default {\n  setup() {\n    const inputRef = ref(null)\n    const listRef = ref(null)\n    const dynamicRef = ref(null)\n    \n    onMounted(() => {\n      // 访问 DOM 元素\n      inputRef.value?.focus()\n      console.log(listRef.value?.offsetHeight)\n    })\n    \n    // 函数式引用\n    const assignRef = el => {\n      if (el) {\n        console.log('元素已赋值', el)\n      } else {\n        console.log('元素已移除')\n      }\n    }\n    \n    return {\n      inputRef,\n      listRef,\n      dynamicRef,\n      assignRef,\n    }\n  },\n  \n  template: \\`\n    \u003Cdiv>\n      \u003Cinput ref=\"inputRef\" />\n      \u003Cul ref=\"listRef\">\n        \u003Cli v-for=\"item in items\" :key=\"item\">{{ item }}\u003C/li>\n      \u003C/ul>\n      \u003Cdiv :ref=\"assignRef\">\u003C/div>\n    \u003C/div>\n  \\`,\n}\n",[1056],{"type":27,"tag":260,"props":1057,"children":1058},{"__ignoreMap":7},[1059],{"type":33,"value":1054},{"type":27,"tag":28,"props":1061,"children":1063},{"id":1062},"依赖注入",[1064],{"type":33,"value":1062},{"type":27,"tag":645,"props":1066,"children":1068},{"id":1067},"跨组件共享数据",[1069],{"type":33,"value":1067},{"type":27,"tag":252,"props":1071,"children":1074},{"className":1072,"code":1073,"language":656,"meta":7},[654],"import { provide, inject, ref, readonly } from 'vue'\n\n// 父组件\nexport default {\n  setup() {\n    const theme = ref('light')\n    const user = ref({ name: 'John', role: 'admin' })\n    \n    // 提供数据给子组件\n    provide('theme', readonly(theme))\n    provide('updateTheme', (newTheme) => {\n      theme.value = newTheme\n    })\n    \n    // 使用 Symbol 作为 key 避免命名冲突\n    const userKey = Symbol()\n    provide(userKey, readonly(user))\n    \n    return {\n      theme,\n      updateTheme: (newTheme) => {\n        theme.value = newTheme\n      },\n    }\n  },\n}\n\n// 子组件\nexport default {\n  setup() {\n    // 注入数据\n    const theme = inject('theme')\n    const updateTheme = inject('updateTheme')\n    const user = inject(Symbol.for('user'))\n    \n    // 带默认值的注入\n    const config = inject('config', {\n      apiUrl: 'http://localhost:3000',\n    })\n    \n    return {\n      theme,\n      updateTheme,\n      user,\n      config,\n    }\n  },\n}\n",[1075],{"type":27,"tag":260,"props":1076,"children":1077},{"__ignoreMap":7},[1078],{"type":33,"value":1073},{"type":27,"tag":28,"props":1080,"children":1082},{"id":1081},"高级状态管理",[1083],{"type":33,"value":1081},{"type":27,"tag":645,"props":1085,"children":1087},{"id":1086},"创建小型-store",[1088],{"type":33,"value":1089},"创建小型 store",{"type":27,"tag":252,"props":1091,"children":1094},{"className":1092,"code":1093,"language":656,"meta":7},[654],"import { reactive, readonly, computed } from 'vue'\n\n// store.js - 不依赖 Pinia 的简单 store\nexport function createStore() {\n  const state = reactive({\n    items: [],\n    filter: 'all',\n    sortBy: 'date',\n  })\n  \n  const filteredItems = computed(() => {\n    let result = state.items\n    \n    if (state.filter !== 'all') {\n      result = result.filter(item => item.status === state.filter)\n    }\n    \n    if (state.sortBy === 'date') {\n      result.sort((a, b) => new Date(b.date) - new Date(a.date))\n    } else if (state.sortBy === 'name') {\n      result.sort((a, b) => a.name.localeCompare(b.name))\n    }\n    \n    return result\n  })\n  \n  const actions = {\n    addItem(item) {\n      state.items.push({ ...item, id: Date.now() })\n    },\n    \n    removeItem(id) {\n      state.items = state.items.filter(item => item.id !== id)\n    },\n    \n    updateItem(id, updates) {\n      const item = state.items.find(item => item.id === id)\n      if (item) {\n        Object.assign(item, updates)\n      }\n    },\n    \n    setFilter(filter) {\n      state.filter = filter\n    },\n    \n    setSortBy(sortBy) {\n      state.sortBy = sortBy\n    },\n  }\n  \n  return {\n    state: readonly(state),\n    filteredItems,\n    ...actions,\n  }\n}\n\n// 使用\nexport default {\n  setup() {\n    const store = createStore()\n    \n    const handleAdd = (item) => {\n      store.addItem(item)\n    }\n    \n    return {\n      items: store.filteredItems,\n      addItem: handleAdd,\n      setFilter: store.setFilter,\n    }\n  },\n}\n",[1095],{"type":27,"tag":260,"props":1096,"children":1097},{"__ignoreMap":7},[1098],{"type":33,"value":1093},{"type":27,"tag":28,"props":1100,"children":1101},{"id":780},[1102],{"type":33,"value":780},{"type":27,"tag":35,"props":1104,"children":1105},{},[1106,1107,1111],{"type":33,"value":787},{"type":27,"tag":789,"props":1108,"children":1109},{},[1110],{"type":33,"value":793},{"type":33,"value":795},{"type":27,"tag":185,"props":1113,"children":1114},{},[1115,1120,1125,1130,1135],{"type":27,"tag":189,"props":1116,"children":1117},{},[1118],{"type":33,"value":1119},"将相关逻辑组织在一起",{"type":27,"tag":189,"props":1121,"children":1122},{},[1123],{"type":33,"value":1124},"创建可重用的组合式函数",{"type":27,"tag":189,"props":1126,"children":1127},{},[1128],{"type":33,"value":1129},"使用 TypeScript 获得更好的类型检查",{"type":27,"tag":189,"props":1131,"children":1132},{},[1133],{"type":33,"value":1134},"合理使用计算属性和监听器",{"type":27,"tag":189,"props":1136,"children":1137},{},[1138],{"type":33,"value":1139},"及时清理事件监听器和定时器",{"type":27,"tag":35,"props":1141,"children":1142},{},[1143,1144,1148],{"type":33,"value":828},{"type":27,"tag":789,"props":1145,"children":1146},{},[1147],{"type":33,"value":833},{"type":33,"value":795},{"type":27,"tag":185,"props":1150,"children":1151},{},[1152,1157,1162,1167,1172],{"type":27,"tag":189,"props":1153,"children":1154},{},[1155],{"type":33,"value":1156},"在 setup 中执行副作用操作（除了生命周期钩子）",{"type":27,"tag":189,"props":1158,"children":1159},{},[1160],{"type":33,"value":1161},"过度使用计算属性",{"type":27,"tag":189,"props":1163,"children":1164},{},[1165],{"type":33,"value":1166},"忘记清理 watch 监听器",{"type":27,"tag":189,"props":1168,"children":1169},{},[1170],{"type":33,"value":1171},"在 reactive 对象中存储引用类型时不谨慎",{"type":27,"tag":189,"props":1173,"children":1174},{},[1175],{"type":33,"value":1176},"过度复杂化组合式函数",{"type":27,"tag":28,"props":1178,"children":1179},{"id":865},[1180],{"type":33,"value":865},{"type":27,"tag":185,"props":1182,"children":1184},{"className":1183},[871],[1185,1194,1203,1212,1221],{"type":27,"tag":189,"props":1186,"children":1188},{"className":1187},[876],[1189,1192],{"type":27,"tag":879,"props":1190,"children":1191},{"disabled":881,"type":882},[],{"type":33,"value":1193}," 正确使用 ref 和 reactive",{"type":27,"tag":189,"props":1195,"children":1197},{"className":1196},[876],[1198,1201],{"type":27,"tag":879,"props":1199,"children":1200},{"disabled":881,"type":882},[],{"type":33,"value":1202}," 生命周期钩子正确",{"type":27,"tag":189,"props":1204,"children":1206},{"className":1205},[876],[1207,1210],{"type":27,"tag":879,"props":1208,"children":1209},{"disabled":881,"type":882},[],{"type":33,"value":1211}," 模板引用工作正常",{"type":27,"tag":189,"props":1213,"children":1215},{"className":1214},[876],[1216,1219],{"type":27,"tag":879,"props":1217,"children":1218},{"disabled":881,"type":882},[],{"type":33,"value":1220}," 组合式函数可重用",{"type":27,"tag":189,"props":1222,"children":1224},{"className":1223},[876],[1225,1228],{"type":27,"tag":879,"props":1226,"children":1227},{"disabled":881,"type":882},[],{"type":33,"value":912},{"title":7,"searchDepth":592,"depth":592,"links":1230},[1231,1232,1236,1239,1242,1245,1248,1251,1252],{"id":960,"depth":595,"text":946},{"id":970,"depth":595,"text":970,"children":1233},[1234,1235],{"id":975,"depth":592,"text":978},{"id":990,"depth":592,"text":990},{"id":951,"depth":595,"text":951,"children":1237},[1238],{"id":1008,"depth":592,"text":1008},{"id":1022,"depth":595,"text":1022,"children":1240},[1241],{"id":1027,"depth":592,"text":1030},{"id":1042,"depth":595,"text":1042,"children":1243},[1244],{"id":1047,"depth":592,"text":1050},{"id":1062,"depth":595,"text":1062,"children":1246},[1247],{"id":1067,"depth":592,"text":1067},{"id":1081,"depth":595,"text":1081,"children":1249},[1250],{"id":1086,"depth":592,"text":1089},{"id":780,"depth":595,"text":780},{"id":865,"depth":595,"text":865},"content:topics:frontend:vue3-composition-api.md","topics/frontend/vue3-composition-api.md","topics/frontend/vue3-composition-api",{"_path":1257,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1258,"description":1259,"date":1260,"topic":5,"author":11,"tags":1261,"image":1267,"featured":881,"readingTime":1268,"body":1269,"_type":605,"_id":2524,"_source":607,"_file":2525,"_stem":2526,"_extension":610},"/topics/frontend/rspack-performance-practice","Rspack 构建性能实战","从 Rspack 的架构设计与编译管线出发，系统对比 Webpack/Vite 并给出 Rspack 在大型项目中的迁移路径、性能调优策略与生产级可观测方案。","2026-01-20",[1262,1263,1264,1265,1266],"Rspack","构建工具","性能优化","Webpack","前端工程化","/images/topics/rspack.jpg",25,{"type":24,"children":1270,"toc":2491},[1271,1276,1288,1293,1311,1323,1328,1351,1354,1360,1365,1371,1404,1410,1428,1431,1437,1443,1448,1461,1467,1472,1485,1491,1509,1514,1517,1523,1528,1663,1668,1691,1694,1700,1706,1711,1716,1783,1796,1802,1810,1823,1831,1844,1852,1865,1871,1889,1892,1898,1904,1909,1922,1928,1933,1944,1950,1955,1973,1978,2006,2009,2015,2020,2031,2036,2054,2059,2077,2080,2086,2091,2109,2114,2137,2142,2160,2163,2169,2175,2180,2198,2204,2222,2228,2246,2249,2255,2370,2375,2393,2396,2402,2460,2463,2468,2473],{"type":27,"tag":28,"props":1272,"children":1274},{"id":1273},"rspack-构建性能实战",[1275],{"type":33,"value":1258},{"type":27,"tag":35,"props":1277,"children":1278},{},[1279,1281,1286],{"type":33,"value":1280},"Rspack 不是\"又一个构建工具\"，而是字节跳动在处理",{"type":27,"tag":789,"props":1282,"children":1283},{},[1284],{"type":33,"value":1285},"超大规模前端项目",{"type":33,"value":1287},"时，对 Webpack 生态的 Rust 重写与工程化沉淀。",{"type":27,"tag":35,"props":1289,"children":1290},{},[1291],{"type":33,"value":1292},"它要解决的核心问题是：",{"type":27,"tag":185,"props":1294,"children":1295},{},[1296,1301,1306],{"type":27,"tag":189,"props":1297,"children":1298},{},[1299],{"type":33,"value":1300},"Webpack 的构建速度在大型 monorepo 下（10k+ 模块）已成为开发体验瓶颈",{"type":27,"tag":189,"props":1302,"children":1303},{},[1304],{"type":33,"value":1305},"但你又无法抛弃 Webpack 的插件生态与配置范式",{"type":27,"tag":189,"props":1307,"children":1308},{},[1309],{"type":33,"value":1310},"Vite 虽然快，但在某些场景（大型遗留项目、特定插件依赖）迁移成本高",{"type":27,"tag":35,"props":1312,"children":1313},{},[1314,1316,1321],{"type":33,"value":1315},"Rspack 的定位是：",{"type":27,"tag":789,"props":1317,"children":1318},{},[1319],{"type":33,"value":1320},"Webpack 兼容 API + Rust 性能 + 生产级稳定性",{"type":33,"value":1322},"。",{"type":27,"tag":35,"props":1324,"children":1325},{},[1326],{"type":33,"value":1327},"这篇文章不讲\"Hello World\"，而是按\"你要在生产上稳定用 Rspack\"的标准，给出：",{"type":27,"tag":185,"props":1329,"children":1330},{},[1331,1336,1341,1346],{"type":27,"tag":189,"props":1332,"children":1333},{},[1334],{"type":33,"value":1335},"性能收益的真实量化方法",{"type":27,"tag":189,"props":1337,"children":1338},{},[1339],{"type":33,"value":1340},"迁移路径与兼容性边界",{"type":27,"tag":189,"props":1342,"children":1343},{},[1344],{"type":33,"value":1345},"优化策略（缓存、并行、Tree Shaking）",{"type":27,"tag":189,"props":1347,"children":1348},{},[1349],{"type":33,"value":1350},"监控与排障（为什么变慢、为什么产物变大）",{"type":27,"tag":46,"props":1352,"children":1353},{},[],{"type":27,"tag":28,"props":1355,"children":1357},{"id":1356},"_1-先回答什么项目值得迁移-rspack",[1358],{"type":33,"value":1359},"1. 先回答：什么项目值得迁移 Rspack？",{"type":27,"tag":35,"props":1361,"children":1362},{},[1363],{"type":33,"value":1364},"不是所有项目都需要 Rspack。",{"type":27,"tag":645,"props":1366,"children":1368},{"id":1367},"_11-高收益场景",[1369],{"type":33,"value":1370},"1.1 高收益场景",{"type":27,"tag":185,"props":1372,"children":1373},{},[1374,1384,1394],{"type":27,"tag":189,"props":1375,"children":1376},{},[1377,1382],{"type":27,"tag":789,"props":1378,"children":1379},{},[1380],{"type":33,"value":1381},"大型 monorepo",{"type":33,"value":1383},"（5k+ 模块，构建时间 > 2 分钟）",{"type":27,"tag":189,"props":1385,"children":1386},{},[1387,1392],{"type":27,"tag":789,"props":1388,"children":1389},{},[1390],{"type":33,"value":1391},"频繁开发迭代",{"type":33,"value":1393},"（HMR 延迟影响体验）",{"type":27,"tag":189,"props":1395,"children":1396},{},[1397,1402],{"type":27,"tag":789,"props":1398,"children":1399},{},[1400],{"type":33,"value":1401},"CI 构建成本高",{"type":33,"value":1403},"（每次 PR 构建超 10 分钟）",{"type":27,"tag":645,"props":1405,"children":1407},{"id":1406},"_12-收益不明显的场景",[1408],{"type":33,"value":1409},"1.2 收益不明显的场景",{"type":27,"tag":185,"props":1411,"children":1412},{},[1413,1418,1423],{"type":27,"tag":189,"props":1414,"children":1415},{},[1416],{"type":33,"value":1417},"小型项目（\u003C 1k 模块）",{"type":27,"tag":189,"props":1419,"children":1420},{},[1421],{"type":33,"value":1422},"已经用 Vite 且体验良好",{"type":27,"tag":189,"props":1424,"children":1425},{},[1426],{"type":33,"value":1427},"高度定制的 Webpack 插件（迁移成本 > 性能收益）",{"type":27,"tag":46,"props":1429,"children":1430},{},[],{"type":27,"tag":28,"props":1432,"children":1434},{"id":1433},"_2-rspack-的架构为什么能快",[1435],{"type":33,"value":1436},"2. Rspack 的架构：为什么能快？",{"type":27,"tag":645,"props":1438,"children":1440},{"id":1439},"_21-rust-并行编译",[1441],{"type":33,"value":1442},"2.1 Rust 并行编译",{"type":27,"tag":35,"props":1444,"children":1445},{},[1446],{"type":33,"value":1447},"Webpack 是单线程 JavaScript，Rspack 是多线程 Rust。",{"type":27,"tag":185,"props":1449,"children":1450},{},[1451,1456],{"type":27,"tag":189,"props":1452,"children":1453},{},[1454],{"type":33,"value":1455},"模块解析、编译、优化可并行",{"type":27,"tag":189,"props":1457,"children":1458},{},[1459],{"type":33,"value":1460},"I/O 密集型任务（读文件、写产物）异步化",{"type":27,"tag":645,"props":1462,"children":1464},{"id":1463},"_22-更激进的缓存策略",[1465],{"type":33,"value":1466},"2.2 更激进的缓存策略",{"type":27,"tag":35,"props":1468,"children":1469},{},[1470],{"type":33,"value":1471},"Rspack 内置持久化缓存：",{"type":27,"tag":185,"props":1473,"children":1474},{},[1475,1480],{"type":27,"tag":189,"props":1476,"children":1477},{},[1478],{"type":33,"value":1479},"模块级别缓存（类似 Webpack 5 的 cache.type: 'filesystem'）",{"type":27,"tag":189,"props":1481,"children":1482},{},[1483],{"type":33,"value":1484},"但实现更激进：对未变化模块跳过编译",{"type":27,"tag":645,"props":1486,"children":1488},{"id":1487},"_23-内置常用功能减少插件开销",[1489],{"type":33,"value":1490},"2.3 内置常用功能（减少插件开销）",{"type":27,"tag":185,"props":1492,"children":1493},{},[1494,1499,1504],{"type":27,"tag":189,"props":1495,"children":1496},{},[1497],{"type":33,"value":1498},"SWC 替代 Babel（内置 TS/JSX/装饰器）",{"type":27,"tag":189,"props":1500,"children":1501},{},[1502],{"type":33,"value":1503},"CSS Modules、PostCSS 内置",{"type":27,"tag":189,"props":1505,"children":1506},{},[1507],{"type":33,"value":1508},"Tree Shaking 内置",{"type":27,"tag":35,"props":1510,"children":1511},{},[1512],{"type":33,"value":1513},"这让 Rspack 在相同功能下比 Webpack + 插件链路更快。",{"type":27,"tag":46,"props":1515,"children":1516},{},[],{"type":27,"tag":28,"props":1518,"children":1520},{"id":1519},"_3-性能对比真实场景的量化",[1521],{"type":33,"value":1522},"3. 性能对比：真实场景的量化",{"type":27,"tag":35,"props":1524,"children":1525},{},[1526],{"type":33,"value":1527},"我们用一个典型中型项目（3k 模块，React + TS + CSS Modules）做对比：",{"type":27,"tag":61,"props":1529,"children":1530},{},[1531,1556],{"type":27,"tag":65,"props":1532,"children":1533},{},[1534],{"type":27,"tag":69,"props":1535,"children":1536},{},[1537,1542,1547,1551],{"type":27,"tag":73,"props":1538,"children":1539},{},[1540],{"type":33,"value":1541},"指标",{"type":27,"tag":73,"props":1543,"children":1544},{},[1545],{"type":33,"value":1546},"Webpack 5",{"type":27,"tag":73,"props":1548,"children":1549},{},[1550],{"type":33,"value":1262},{"type":27,"tag":73,"props":1552,"children":1553},{},[1554],{"type":33,"value":1555},"提升",{"type":27,"tag":89,"props":1557,"children":1558},{},[1559,1585,1611,1637],{"type":27,"tag":69,"props":1560,"children":1561},{},[1562,1567,1572,1577],{"type":27,"tag":96,"props":1563,"children":1564},{},[1565],{"type":33,"value":1566},"冷启动",{"type":27,"tag":96,"props":1568,"children":1569},{},[1570],{"type":33,"value":1571},"42s",{"type":27,"tag":96,"props":1573,"children":1574},{},[1575],{"type":33,"value":1576},"8s",{"type":27,"tag":96,"props":1578,"children":1579},{},[1580],{"type":27,"tag":789,"props":1581,"children":1582},{},[1583],{"type":33,"value":1584},"5.2x",{"type":27,"tag":69,"props":1586,"children":1587},{},[1588,1593,1598,1603],{"type":27,"tag":96,"props":1589,"children":1590},{},[1591],{"type":33,"value":1592},"HMR（热更新）",{"type":27,"tag":96,"props":1594,"children":1595},{},[1596],{"type":33,"value":1597},"1.2s",{"type":27,"tag":96,"props":1599,"children":1600},{},[1601],{"type":33,"value":1602},"0.15s",{"type":27,"tag":96,"props":1604,"children":1605},{},[1606],{"type":27,"tag":789,"props":1607,"children":1608},{},[1609],{"type":33,"value":1610},"8x",{"type":27,"tag":69,"props":1612,"children":1613},{},[1614,1619,1624,1629],{"type":27,"tag":96,"props":1615,"children":1616},{},[1617],{"type":33,"value":1618},"生产构建",{"type":27,"tag":96,"props":1620,"children":1621},{},[1622],{"type":33,"value":1623},"125s",{"type":27,"tag":96,"props":1625,"children":1626},{},[1627],{"type":33,"value":1628},"28s",{"type":27,"tag":96,"props":1630,"children":1631},{},[1632],{"type":27,"tag":789,"props":1633,"children":1634},{},[1635],{"type":33,"value":1636},"4.5x",{"type":27,"tag":69,"props":1638,"children":1639},{},[1640,1645,1650,1655],{"type":27,"tag":96,"props":1641,"children":1642},{},[1643],{"type":33,"value":1644},"内存峰值",{"type":27,"tag":96,"props":1646,"children":1647},{},[1648],{"type":33,"value":1649},"1.8GB",{"type":27,"tag":96,"props":1651,"children":1652},{},[1653],{"type":33,"value":1654},"0.9GB",{"type":27,"tag":96,"props":1656,"children":1657},{},[1658],{"type":27,"tag":789,"props":1659,"children":1660},{},[1661],{"type":33,"value":1662},"2x",{"type":27,"tag":35,"props":1664,"children":1665},{},[1666],{"type":33,"value":1667},"关键收益：",{"type":27,"tag":185,"props":1669,"children":1670},{},[1671,1681],{"type":27,"tag":189,"props":1672,"children":1673},{},[1674,1679],{"type":27,"tag":789,"props":1675,"children":1676},{},[1677],{"type":33,"value":1678},"开发体验质变",{"type":33,"value":1680},"（HMR \u003C 200ms）",{"type":27,"tag":189,"props":1682,"children":1683},{},[1684,1689],{"type":27,"tag":789,"props":1685,"children":1686},{},[1687],{"type":33,"value":1688},"CI 成本减半",{"type":33,"value":1690},"（构建时间直接影响 Runner 费用）",{"type":27,"tag":46,"props":1692,"children":1693},{},[],{"type":27,"tag":28,"props":1695,"children":1697},{"id":1696},"_4-迁移路径从-webpack-到-rspack",[1698],{"type":33,"value":1699},"4. 迁移路径：从 Webpack 到 Rspack",{"type":27,"tag":645,"props":1701,"children":1703},{"id":1702},"_41-最小迁移保守策略",[1704],{"type":33,"value":1705},"4.1 最小迁移（保守策略）",{"type":27,"tag":35,"props":1707,"children":1708},{},[1709],{"type":33,"value":1710},"目标：用最小改动换取性能收益。",{"type":27,"tag":35,"props":1712,"children":1713},{},[1714],{"type":33,"value":1715},"步骤：",{"type":27,"tag":378,"props":1717,"children":1718},{},[1719,1738,1757,1778],{"type":27,"tag":189,"props":1720,"children":1721},{},[1722,1724,1730,1732],{"type":33,"value":1723},"安装 ",{"type":27,"tag":260,"props":1725,"children":1727},{"className":1726},[],[1728],{"type":33,"value":1729},"@rspack/cli",{"type":33,"value":1731}," 与 ",{"type":27,"tag":260,"props":1733,"children":1735},{"className":1734},[],[1736],{"type":33,"value":1737},"@rspack/core",{"type":27,"tag":189,"props":1739,"children":1740},{},[1741,1743,1749,1751],{"type":33,"value":1742},"把 ",{"type":27,"tag":260,"props":1744,"children":1746},{"className":1745},[],[1747],{"type":33,"value":1748},"webpack.config.js",{"type":33,"value":1750}," 改为 ",{"type":27,"tag":260,"props":1752,"children":1754},{"className":1753},[],[1755],{"type":33,"value":1756},"rspack.config.js",{"type":27,"tag":189,"props":1758,"children":1759},{},[1760,1762,1768,1770,1776],{"type":33,"value":1761},"替换构建命令（",{"type":27,"tag":260,"props":1763,"children":1765},{"className":1764},[],[1766],{"type":33,"value":1767},"rspack build",{"type":33,"value":1769}," / ",{"type":27,"tag":260,"props":1771,"children":1773},{"className":1772},[],[1774],{"type":33,"value":1775},"rspack dev",{"type":33,"value":1777},"）",{"type":27,"tag":189,"props":1779,"children":1780},{},[1781],{"type":33,"value":1782},"运行并修复兼容性问题",{"type":27,"tag":35,"props":1784,"children":1785},{},[1786,1788,1794],{"type":33,"value":1787},"预计迁移成本：1",{"type":27,"tag":1789,"props":1790,"children":1791},"del",{},[1792],{"type":33,"value":1793},"2 天（小型项目）/ 1",{"type":33,"value":1795},"2 周（大型项目）",{"type":27,"tag":645,"props":1797,"children":1799},{"id":1798},"_42-兼容性边界哪些需要调整",[1800],{"type":33,"value":1801},"4.2 兼容性边界：哪些需要调整",{"type":27,"tag":35,"props":1803,"children":1804},{},[1805],{"type":27,"tag":789,"props":1806,"children":1807},{},[1808],{"type":33,"value":1809},"插件兼容",{"type":27,"tag":185,"props":1811,"children":1812},{},[1813,1818],{"type":27,"tag":189,"props":1814,"children":1815},{},[1816],{"type":33,"value":1817},"Rspack 支持大部分 Webpack 插件（API 兼容）",{"type":27,"tag":189,"props":1819,"children":1820},{},[1821],{"type":33,"value":1822},"但少数复杂插件（例如深度依赖 Webpack 内部 API）需要适配",{"type":27,"tag":35,"props":1824,"children":1825},{},[1826],{"type":27,"tag":789,"props":1827,"children":1828},{},[1829],{"type":33,"value":1830},"Loader 兼容",{"type":27,"tag":185,"props":1832,"children":1833},{},[1834,1839],{"type":27,"tag":189,"props":1835,"children":1836},{},[1837],{"type":33,"value":1838},"常用 loader（babel-loader、css-loader、postcss-loader）兼容",{"type":27,"tag":189,"props":1840,"children":1841},{},[1842],{"type":33,"value":1843},"部分自定义 loader 需要测试",{"type":27,"tag":35,"props":1845,"children":1846},{},[1847],{"type":27,"tag":789,"props":1848,"children":1849},{},[1850],{"type":33,"value":1851},"配置差异",{"type":27,"tag":185,"props":1853,"children":1854},{},[1855,1860],{"type":27,"tag":189,"props":1856,"children":1857},{},[1858],{"type":33,"value":1859},"resolve、output、optimization 等配置与 Webpack 高度一致",{"type":27,"tag":189,"props":1861,"children":1862},{},[1863],{"type":33,"value":1864},"少数高级配置需要查文档",{"type":27,"tag":645,"props":1866,"children":1868},{"id":1867},"_43-推荐的迁移节奏",[1869],{"type":33,"value":1870},"4.3 推荐的迁移节奏",{"type":27,"tag":185,"props":1872,"children":1873},{},[1874,1879,1884],{"type":27,"tag":189,"props":1875,"children":1876},{},[1877],{"type":33,"value":1878},"Week 1：本地开发环境先行",{"type":27,"tag":189,"props":1880,"children":1881},{},[1882],{"type":33,"value":1883},"Week 2：CI 构建切换（并保留 Webpack 作为 fallback）",{"type":27,"tag":189,"props":1885,"children":1886},{},[1887],{"type":33,"value":1888},"Week 3~4：生产构建切换并观测",{"type":27,"tag":46,"props":1890,"children":1891},{},[],{"type":27,"tag":28,"props":1893,"children":1895},{"id":1894},"_5-性能调优让-rspack-更快",[1896],{"type":33,"value":1897},"5. 性能调优：让 Rspack 更快",{"type":27,"tag":645,"props":1899,"children":1901},{"id":1900},"_51-缓存策略",[1902],{"type":33,"value":1903},"5.1 缓存策略",{"type":27,"tag":35,"props":1905,"children":1906},{},[1907],{"type":33,"value":1908},"默认缓存已经很激进，但你可以：",{"type":27,"tag":185,"props":1910,"children":1911},{},[1912,1917],{"type":27,"tag":189,"props":1913,"children":1914},{},[1915],{"type":33,"value":1916},"显式配置缓存目录（例如挂载 SSD）",{"type":27,"tag":189,"props":1918,"children":1919},{},[1920],{"type":33,"value":1921},"在 CI 上持久化缓存（例如用 actions/cache）",{"type":27,"tag":645,"props":1923,"children":1925},{"id":1924},"_52-并行度调优",[1926],{"type":33,"value":1927},"5.2 并行度调优",{"type":27,"tag":35,"props":1929,"children":1930},{},[1931],{"type":33,"value":1932},"Rspack 默认会用所有 CPU 核心，但在容器环境（例如 CI）可能需要限制：",{"type":27,"tag":252,"props":1934,"children":1939},{"className":1935,"code":1937,"language":1938,"meta":7},[1936],"language-js","module.exports = {\n  experiments: {\n    rspackFuture: {\n      disableTransformByDefault: true, // 减少不必要转换\n    },\n  },\n}\n","js",[1940],{"type":27,"tag":260,"props":1941,"children":1942},{"__ignoreMap":7},[1943],{"type":33,"value":1937},{"type":27,"tag":645,"props":1945,"children":1947},{"id":1946},"_53-tree-shaking-与-dead-code-elimination",[1948],{"type":33,"value":1949},"5.3 Tree Shaking 与 Dead Code Elimination",{"type":27,"tag":35,"props":1951,"children":1952},{},[1953],{"type":33,"value":1954},"Rspack 内置 Tree Shaking，但效果取决于：",{"type":27,"tag":185,"props":1956,"children":1957},{},[1958,1963,1968],{"type":27,"tag":189,"props":1959,"children":1960},{},[1961],{"type":33,"value":1962},"是否使用 ESM（而非 CommonJS）",{"type":27,"tag":189,"props":1964,"children":1965},{},[1966],{"type":33,"value":1967},"副作用标记（sideEffects: false）",{"type":27,"tag":189,"props":1969,"children":1970},{},[1971],{"type":33,"value":1972},"动态 import 的拆分策略",{"type":27,"tag":35,"props":1974,"children":1975},{},[1976],{"type":33,"value":1977},"建议：",{"type":27,"tag":185,"props":1979,"children":1980},{},[1981,1994],{"type":27,"tag":189,"props":1982,"children":1983},{},[1984,1986,1992],{"type":33,"value":1985},"对第三方库检查 ",{"type":27,"tag":260,"props":1987,"children":1989},{"className":1988},[],[1990],{"type":33,"value":1991},"sideEffects",{"type":33,"value":1993}," 配置",{"type":27,"tag":189,"props":1995,"children":1996},{},[1997,1999,2005],{"type":33,"value":1998},"避免\"全量引入后 tree shake\"（例如 ",{"type":27,"tag":260,"props":2000,"children":2002},{"className":2001},[],[2003],{"type":33,"value":2004},"import * from 'lodash'",{"type":33,"value":1777},{"type":27,"tag":46,"props":2007,"children":2008},{},[],{"type":27,"tag":28,"props":2010,"children":2012},{"id":2011},"_6-产物分析与优化",[2013],{"type":33,"value":2014},"6. 产物分析与优化",{"type":27,"tag":35,"props":2016,"children":2017},{},[2018],{"type":33,"value":2019},"Rspack 提供内置分析工具：",{"type":27,"tag":252,"props":2021,"children":2026},{"className":2022,"code":2024,"language":2025,"meta":7},[2023],"language-bash","rspack build --analyze\n","bash",[2027],{"type":27,"tag":260,"props":2028,"children":2029},{"__ignoreMap":7},[2030],{"type":33,"value":2024},{"type":27,"tag":35,"props":2032,"children":2033},{},[2034],{"type":33,"value":2035},"关键指标：",{"type":27,"tag":185,"props":2037,"children":2038},{},[2039,2044,2049],{"type":27,"tag":189,"props":2040,"children":2041},{},[2042],{"type":33,"value":2043},"各 chunk 体积分布",{"type":27,"tag":189,"props":2045,"children":2046},{},[2047],{"type":33,"value":2048},"重复依赖（例如多个版本的 lodash）",{"type":27,"tag":189,"props":2050,"children":2051},{},[2052],{"type":33,"value":2053},"未被 tree shake 的代码",{"type":27,"tag":35,"props":2055,"children":2056},{},[2057],{"type":33,"value":2058},"优化策略：",{"type":27,"tag":185,"props":2060,"children":2061},{},[2062,2067,2072],{"type":27,"tag":189,"props":2063,"children":2064},{},[2065],{"type":33,"value":2066},"拆分 vendor chunk（按更新频率）",{"type":27,"tag":189,"props":2068,"children":2069},{},[2070],{"type":33,"value":2071},"对大型库按需引入（例如 antd/lodash-es）",{"type":27,"tag":189,"props":2073,"children":2074},{},[2075],{"type":33,"value":2076},"检查动态 import 的粒度",{"type":27,"tag":46,"props":2078,"children":2079},{},[],{"type":27,"tag":28,"props":2081,"children":2083},{"id":2082},"_7-生产可观测性让构建可量化",[2084],{"type":33,"value":2085},"7. 生产可观测性：让构建可量化",{"type":27,"tag":35,"props":2087,"children":2088},{},[2089],{"type":33,"value":2090},"在 CI/CD 里，你需要能回答：",{"type":27,"tag":185,"props":2092,"children":2093},{},[2094,2099,2104],{"type":27,"tag":189,"props":2095,"children":2096},{},[2097],{"type":33,"value":2098},"这次构建为什么变慢？",{"type":27,"tag":189,"props":2100,"children":2101},{},[2102],{"type":33,"value":2103},"产物为什么变大？",{"type":27,"tag":189,"props":2105,"children":2106},{},[2107],{"type":33,"value":2108},"哪个模块耗时最多？",{"type":27,"tag":35,"props":2110,"children":2111},{},[2112],{"type":33,"value":2113},"建议在 CI 里记录：",{"type":27,"tag":185,"props":2115,"children":2116},{},[2117,2122,2127,2132],{"type":27,"tag":189,"props":2118,"children":2119},{},[2120],{"type":33,"value":2121},"构建总耗时",{"type":27,"tag":189,"props":2123,"children":2124},{},[2125],{"type":33,"value":2126},"各阶段耗时（resolve、compile、optimize、emit）",{"type":27,"tag":189,"props":2128,"children":2129},{},[2130],{"type":33,"value":2131},"产物体积（按 chunk）",{"type":27,"tag":189,"props":2133,"children":2134},{},[2135],{"type":33,"value":2136},"缓存命中率",{"type":27,"tag":35,"props":2138,"children":2139},{},[2140],{"type":33,"value":2141},"落地方式：",{"type":27,"tag":185,"props":2143,"children":2144},{},[2145,2150,2155],{"type":27,"tag":189,"props":2146,"children":2147},{},[2148],{"type":33,"value":2149},"用 Rspack 的 stats 输出",{"type":27,"tag":189,"props":2151,"children":2152},{},[2153],{"type":33,"value":2154},"在 CI 日志里保留关键指标",{"type":27,"tag":189,"props":2156,"children":2157},{},[2158],{"type":33,"value":2159},"对产物体积做 baseline 对比（变化 > 5% 报警）",{"type":27,"tag":46,"props":2161,"children":2162},{},[],{"type":27,"tag":28,"props":2164,"children":2166},{"id":2165},"_8-常见问题排查",[2167],{"type":33,"value":2168},"8. 常见问题排查",{"type":27,"tag":645,"props":2170,"children":2172},{"id":2171},"_81-迁移后变慢了",[2173],{"type":33,"value":2174},"8.1 \"迁移后变慢了\"",{"type":27,"tag":35,"props":2176,"children":2177},{},[2178],{"type":33,"value":2179},"排查顺序：",{"type":27,"tag":185,"props":2181,"children":2182},{},[2183,2188,2193],{"type":27,"tag":189,"props":2184,"children":2185},{},[2186],{"type":33,"value":2187},"缓存是否生效（首次构建慢正常）",{"type":27,"tag":189,"props":2189,"children":2190},{},[2191],{"type":33,"value":2192},"是否有 loader 拖慢（例如未优化的自定义 loader）",{"type":27,"tag":189,"props":2194,"children":2195},{},[2196],{"type":33,"value":2197},"并行度是否受限（例如 CI 限制 CPU）",{"type":27,"tag":645,"props":2199,"children":2201},{"id":2200},"_82-产物体积变大了",[2202],{"type":33,"value":2203},"8.2 \"产物体积变大了\"",{"type":27,"tag":185,"props":2205,"children":2206},{},[2207,2212,2217],{"type":27,"tag":189,"props":2208,"children":2209},{},[2210],{"type":33,"value":2211},"检查 Tree Shaking 是否生效",{"type":27,"tag":189,"props":2213,"children":2214},{},[2215],{"type":33,"value":2216},"检查是否引入了更多 polyfill",{"type":27,"tag":189,"props":2218,"children":2219},{},[2220],{"type":33,"value":2221},"对比 chunk 分布（用 analyze）",{"type":27,"tag":645,"props":2223,"children":2225},{"id":2224},"_83-某些模块编译失败",[2226],{"type":33,"value":2227},"8.3 \"某些模块编译失败\"",{"type":27,"tag":185,"props":2229,"children":2230},{},[2231,2236,2241],{"type":27,"tag":189,"props":2232,"children":2233},{},[2234],{"type":33,"value":2235},"检查是否依赖 Webpack 特定 API",{"type":27,"tag":189,"props":2237,"children":2238},{},[2239],{"type":33,"value":2240},"查看 Rspack 官方兼容性列表",{"type":27,"tag":189,"props":2242,"children":2243},{},[2244],{"type":33,"value":2245},"在 GitHub Issues 搜索类似问题",{"type":27,"tag":46,"props":2247,"children":2248},{},[],{"type":27,"tag":28,"props":2250,"children":2252},{"id":2251},"_9-rspack-vs-vite什么时候选哪个",[2253],{"type":33,"value":2254},"9. Rspack vs Vite：什么时候选哪个？",{"type":27,"tag":61,"props":2256,"children":2257},{},[2258,2278],{"type":27,"tag":65,"props":2259,"children":2260},{},[2261],{"type":27,"tag":69,"props":2262,"children":2263},{},[2264,2269,2273],{"type":27,"tag":73,"props":2265,"children":2266},{},[2267],{"type":33,"value":2268},"维度",{"type":27,"tag":73,"props":2270,"children":2271},{},[2272],{"type":33,"value":1262},{"type":27,"tag":73,"props":2274,"children":2275},{},[2276],{"type":33,"value":2277},"Vite",{"type":27,"tag":89,"props":2279,"children":2280},{},[2281,2299,2316,2334,2352],{"type":27,"tag":69,"props":2282,"children":2283},{},[2284,2289,2294],{"type":27,"tag":96,"props":2285,"children":2286},{},[2287],{"type":33,"value":2288},"开发速度",{"type":27,"tag":96,"props":2290,"children":2291},{},[2292],{"type":33,"value":2293},"极快（Rust 编译）",{"type":27,"tag":96,"props":2295,"children":2296},{},[2297],{"type":33,"value":2298},"极快（ESM 直连）",{"type":27,"tag":69,"props":2300,"children":2301},{},[2302,2306,2311],{"type":27,"tag":96,"props":2303,"children":2304},{},[2305],{"type":33,"value":1618},{"type":27,"tag":96,"props":2307,"children":2308},{},[2309],{"type":33,"value":2310},"快（全量编译优化）",{"type":27,"tag":96,"props":2312,"children":2313},{},[2314],{"type":33,"value":2315},"快（Rollup）",{"type":27,"tag":69,"props":2317,"children":2318},{},[2319,2324,2329],{"type":27,"tag":96,"props":2320,"children":2321},{},[2322],{"type":33,"value":2323},"Webpack 兼容",{"type":27,"tag":96,"props":2325,"children":2326},{},[2327],{"type":33,"value":2328},"高",{"type":27,"tag":96,"props":2330,"children":2331},{},[2332],{"type":33,"value":2333},"低",{"type":27,"tag":69,"props":2335,"children":2336},{},[2337,2342,2347],{"type":27,"tag":96,"props":2338,"children":2339},{},[2340],{"type":33,"value":2341},"插件生态",{"type":27,"tag":96,"props":2343,"children":2344},{},[2345],{"type":33,"value":2346},"Webpack 生态",{"type":27,"tag":96,"props":2348,"children":2349},{},[2350],{"type":33,"value":2351},"Rollup/Vite 生态",{"type":27,"tag":69,"props":2353,"children":2354},{},[2355,2360,2365],{"type":27,"tag":96,"props":2356,"children":2357},{},[2358],{"type":33,"value":2359},"适用项目",{"type":27,"tag":96,"props":2361,"children":2362},{},[2363],{"type":33,"value":2364},"Webpack 迁移、大型 monorepo",{"type":27,"tag":96,"props":2366,"children":2367},{},[2368],{"type":33,"value":2369},"新项目、中小型",{"type":27,"tag":35,"props":2371,"children":2372},{},[2373],{"type":33,"value":2374},"选择建议：",{"type":27,"tag":185,"props":2376,"children":2377},{},[2378,2383,2388],{"type":27,"tag":189,"props":2379,"children":2380},{},[2381],{"type":33,"value":2382},"新项目：优先 Vite",{"type":27,"tag":189,"props":2384,"children":2385},{},[2386],{"type":33,"value":2387},"Webpack 遗留项目：Rspack",{"type":27,"tag":189,"props":2389,"children":2390},{},[2391],{"type":33,"value":2392},"大型 monorepo + Webpack 依赖：Rspack",{"type":27,"tag":46,"props":2394,"children":2395},{},[],{"type":27,"tag":28,"props":2397,"children":2399},{"id":2398},"_10-上线检查清单",[2400],{"type":33,"value":2401},"10. 上线检查清单",{"type":27,"tag":185,"props":2403,"children":2405},{"className":2404},[871],[2406,2415,2424,2433,2442,2451],{"type":27,"tag":189,"props":2407,"children":2409},{"className":2408},[876],[2410,2413],{"type":27,"tag":879,"props":2411,"children":2412},{"disabled":881,"type":882},[],{"type":33,"value":2414}," 本地开发环境已验证（HMR/热更新正常）",{"type":27,"tag":189,"props":2416,"children":2418},{"className":2417},[876],[2419,2422],{"type":27,"tag":879,"props":2420,"children":2421},{"disabled":881,"type":882},[],{"type":33,"value":2423}," CI 构建已切换并观测 3 天以上",{"type":27,"tag":189,"props":2425,"children":2427},{"className":2426},[876],[2428,2431],{"type":27,"tag":879,"props":2429,"children":2430},{"disabled":881,"type":882},[],{"type":33,"value":2432}," 产物体积对比无异常（baseline ± 5%）",{"type":27,"tag":189,"props":2434,"children":2436},{"className":2435},[876],[2437,2440],{"type":27,"tag":879,"props":2438,"children":2439},{"disabled":881,"type":882},[],{"type":33,"value":2441}," 关键页面功能回归测试通过",{"type":27,"tag":189,"props":2443,"children":2445},{"className":2444},[876],[2446,2449],{"type":27,"tag":879,"props":2447,"children":2448},{"disabled":881,"type":882},[],{"type":33,"value":2450}," 有构建耗时与缓存命中率监控",{"type":27,"tag":189,"props":2452,"children":2454},{"className":2453},[876],[2455,2458],{"type":27,"tag":879,"props":2456,"children":2457},{"disabled":881,"type":882},[],{"type":33,"value":2459}," 有回滚方案（保留 Webpack 配置）",{"type":27,"tag":46,"props":2461,"children":2462},{},[],{"type":27,"tag":28,"props":2464,"children":2466},{"id":2465},"总结",[2467],{"type":33,"value":2465},{"type":27,"tag":35,"props":2469,"children":2470},{},[2471],{"type":33,"value":2472},"Rspack 的核心价值是：",{"type":27,"tag":185,"props":2474,"children":2475},{},[2476,2481,2486],{"type":27,"tag":189,"props":2477,"children":2478},{},[2479],{"type":33,"value":2480},"在 Webpack 生态下获得接近 Vite 的速度",{"type":27,"tag":189,"props":2482,"children":2483},{},[2484],{"type":33,"value":2485},"对大型项目构建成本与开发体验的显著改善",{"type":27,"tag":189,"props":2487,"children":2488},{},[2489],{"type":33,"value":2490},"生产级稳定性（字节跳动内部大规模验证）",{"title":7,"searchDepth":592,"depth":592,"links":2492},[2493,2494,2498,2503,2504,2509,2514,2515,2516,2521,2522,2523],{"id":1273,"depth":595,"text":1258},{"id":1356,"depth":595,"text":1359,"children":2495},[2496,2497],{"id":1367,"depth":592,"text":1370},{"id":1406,"depth":592,"text":1409},{"id":1433,"depth":595,"text":1436,"children":2499},[2500,2501,2502],{"id":1439,"depth":592,"text":1442},{"id":1463,"depth":592,"text":1466},{"id":1487,"depth":592,"text":1490},{"id":1519,"depth":595,"text":1522},{"id":1696,"depth":595,"text":1699,"children":2505},[2506,2507,2508],{"id":1702,"depth":592,"text":1705},{"id":1798,"depth":592,"text":1801},{"id":1867,"depth":592,"text":1870},{"id":1894,"depth":595,"text":1897,"children":2510},[2511,2512,2513],{"id":1900,"depth":592,"text":1903},{"id":1924,"depth":592,"text":1927},{"id":1946,"depth":592,"text":1949},{"id":2011,"depth":595,"text":2014},{"id":2082,"depth":595,"text":2085},{"id":2165,"depth":595,"text":2168,"children":2517},[2518,2519,2520],{"id":2171,"depth":592,"text":2174},{"id":2200,"depth":592,"text":2203},{"id":2224,"depth":592,"text":2227},{"id":2251,"depth":595,"text":2254},{"id":2398,"depth":595,"text":2401},{"id":2465,"depth":595,"text":2465},"content:topics:frontend:rspack-performance-practice.md","topics/frontend/rspack-performance-practice.md","topics/frontend/rspack-performance-practice",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":2528,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":2529,"_type":605,"_id":606,"_source":607,"_file":608,"_stem":609,"_extension":610},[13,14,15,16,17],{"type":24,"children":2530,"toc":2977},[2531,2535,2539,2543,2546,2550,2554,2638,2642,2645,2649,2653,2668,2672,2676,2679,2683,2687,2706,2714,2718,2721,2725,2729,2733,2752,2756,2759,2763,2767,2786,2790,2794,2797,2801,2805,2820,2824,2828,2847,2850,2854,2858,2877,2881,2900,2904,2907,2911,2938,2941,2945,2949,2953],{"type":27,"tag":28,"props":2532,"children":2533},{"id":30},[2534],{"type":33,"value":8},{"type":27,"tag":35,"props":2536,"children":2537},{},[2538],{"type":33,"value":39},{"type":27,"tag":35,"props":2540,"children":2541},{},[2542],{"type":33,"value":44},{"type":27,"tag":46,"props":2544,"children":2545},{},[],{"type":27,"tag":28,"props":2547,"children":2548},{"id":51},[2549],{"type":33,"value":54},{"type":27,"tag":35,"props":2551,"children":2552},{},[2553],{"type":33,"value":59},{"type":27,"tag":61,"props":2555,"children":2556},{},[2557,2575],{"type":27,"tag":65,"props":2558,"children":2559},{},[2560],{"type":27,"tag":69,"props":2561,"children":2562},{},[2563,2567,2571],{"type":27,"tag":73,"props":2564,"children":2565},{},[2566],{"type":33,"value":77},{"type":27,"tag":73,"props":2568,"children":2569},{},[2570],{"type":33,"value":82},{"type":27,"tag":73,"props":2572,"children":2573},{},[2574],{"type":33,"value":87},{"type":27,"tag":89,"props":2576,"children":2577},{},[2578,2593,2608,2623],{"type":27,"tag":69,"props":2579,"children":2580},{},[2581,2585,2589],{"type":27,"tag":96,"props":2582,"children":2583},{},[2584],{"type":33,"value":100},{"type":27,"tag":96,"props":2586,"children":2587},{},[2588],{"type":33,"value":105},{"type":27,"tag":96,"props":2590,"children":2591},{},[2592],{"type":33,"value":110},{"type":27,"tag":69,"props":2594,"children":2595},{},[2596,2600,2604],{"type":27,"tag":96,"props":2597,"children":2598},{},[2599],{"type":33,"value":118},{"type":27,"tag":96,"props":2601,"children":2602},{},[2603],{"type":33,"value":123},{"type":27,"tag":96,"props":2605,"children":2606},{},[2607],{"type":33,"value":128},{"type":27,"tag":69,"props":2609,"children":2610},{},[2611,2615,2619],{"type":27,"tag":96,"props":2612,"children":2613},{},[2614],{"type":33,"value":136},{"type":27,"tag":96,"props":2616,"children":2617},{},[2618],{"type":33,"value":141},{"type":27,"tag":96,"props":2620,"children":2621},{},[2622],{"type":33,"value":146},{"type":27,"tag":69,"props":2624,"children":2625},{},[2626,2630,2634],{"type":27,"tag":96,"props":2627,"children":2628},{},[2629],{"type":33,"value":154},{"type":27,"tag":96,"props":2631,"children":2632},{},[2633],{"type":33,"value":159},{"type":27,"tag":96,"props":2635,"children":2636},{},[2637],{"type":33,"value":164},{"type":27,"tag":35,"props":2639,"children":2640},{},[2641],{"type":33,"value":169},{"type":27,"tag":46,"props":2643,"children":2644},{},[],{"type":27,"tag":28,"props":2646,"children":2647},{"id":175},[2648],{"type":33,"value":178},{"type":27,"tag":35,"props":2650,"children":2651},{},[2652],{"type":33,"value":183},{"type":27,"tag":185,"props":2654,"children":2655},{},[2656,2660,2664],{"type":27,"tag":189,"props":2657,"children":2658},{},[2659],{"type":33,"value":193},{"type":27,"tag":189,"props":2661,"children":2662},{},[2663],{"type":33,"value":198},{"type":27,"tag":189,"props":2665,"children":2666},{},[2667],{"type":33,"value":203},{"type":27,"tag":35,"props":2669,"children":2670},{},[2671],{"type":33,"value":208},{"type":27,"tag":35,"props":2673,"children":2674},{},[2675],{"type":33,"value":213},{"type":27,"tag":46,"props":2677,"children":2678},{},[],{"type":27,"tag":28,"props":2680,"children":2681},{"id":219},[2682],{"type":33,"value":222},{"type":27,"tag":35,"props":2684,"children":2685},{},[2686],{"type":33,"value":227},{"type":27,"tag":185,"props":2688,"children":2689},{},[2690,2694,2698,2702],{"type":27,"tag":189,"props":2691,"children":2692},{},[2693],{"type":33,"value":235},{"type":27,"tag":189,"props":2695,"children":2696},{},[2697],{"type":33,"value":240},{"type":27,"tag":189,"props":2699,"children":2700},{},[2701],{"type":33,"value":245},{"type":27,"tag":189,"props":2703,"children":2704},{},[2705],{"type":33,"value":250},{"type":27,"tag":252,"props":2707,"children":2709},{"className":2708,"code":256,"language":257,"meta":7},[255],[2710],{"type":27,"tag":260,"props":2711,"children":2712},{"__ignoreMap":7},[2713],{"type":33,"value":256},{"type":27,"tag":35,"props":2715,"children":2716},{},[2717],{"type":33,"value":268},{"type":27,"tag":46,"props":2719,"children":2720},{},[],{"type":27,"tag":28,"props":2722,"children":2723},{"id":274},[2724],{"type":33,"value":277},{"type":27,"tag":35,"props":2726,"children":2727},{},[2728],{"type":33,"value":282},{"type":27,"tag":35,"props":2730,"children":2731},{},[2732],{"type":33,"value":287},{"type":27,"tag":185,"props":2734,"children":2735},{},[2736,2740,2744,2748],{"type":27,"tag":189,"props":2737,"children":2738},{},[2739],{"type":33,"value":295},{"type":27,"tag":189,"props":2741,"children":2742},{},[2743],{"type":33,"value":300},{"type":27,"tag":189,"props":2745,"children":2746},{},[2747],{"type":33,"value":305},{"type":27,"tag":189,"props":2749,"children":2750},{},[2751],{"type":33,"value":310},{"type":27,"tag":35,"props":2753,"children":2754},{},[2755],{"type":33,"value":315},{"type":27,"tag":46,"props":2757,"children":2758},{},[],{"type":27,"tag":28,"props":2760,"children":2761},{"id":321},[2762],{"type":33,"value":324},{"type":27,"tag":35,"props":2764,"children":2765},{},[2766],{"type":33,"value":329},{"type":27,"tag":185,"props":2768,"children":2769},{},[2770,2774,2778,2782],{"type":27,"tag":189,"props":2771,"children":2772},{},[2773],{"type":33,"value":337},{"type":27,"tag":189,"props":2775,"children":2776},{},[2777],{"type":33,"value":342},{"type":27,"tag":189,"props":2779,"children":2780},{},[2781],{"type":33,"value":347},{"type":27,"tag":189,"props":2783,"children":2784},{},[2785],{"type":33,"value":352},{"type":27,"tag":35,"props":2787,"children":2788},{},[2789],{"type":33,"value":357},{"type":27,"tag":35,"props":2791,"children":2792},{},[2793],{"type":33,"value":362},{"type":27,"tag":46,"props":2795,"children":2796},{},[],{"type":27,"tag":28,"props":2798,"children":2799},{"id":368},[2800],{"type":33,"value":371},{"type":27,"tag":35,"props":2802,"children":2803},{},[2804],{"type":33,"value":376},{"type":27,"tag":378,"props":2806,"children":2807},{},[2808,2812,2816],{"type":27,"tag":189,"props":2809,"children":2810},{},[2811],{"type":33,"value":385},{"type":27,"tag":189,"props":2813,"children":2814},{},[2815],{"type":33,"value":390},{"type":27,"tag":189,"props":2817,"children":2818},{},[2819],{"type":33,"value":395},{"type":27,"tag":35,"props":2821,"children":2822},{},[2823],{"type":33,"value":400},{"type":27,"tag":35,"props":2825,"children":2826},{},[2827],{"type":33,"value":405},{"type":27,"tag":185,"props":2829,"children":2830},{},[2831,2835,2839,2843],{"type":27,"tag":189,"props":2832,"children":2833},{},[2834],{"type":33,"value":413},{"type":27,"tag":189,"props":2836,"children":2837},{},[2838],{"type":33,"value":418},{"type":27,"tag":189,"props":2840,"children":2841},{},[2842],{"type":33,"value":423},{"type":27,"tag":189,"props":2844,"children":2845},{},[2846],{"type":33,"value":428},{"type":27,"tag":46,"props":2848,"children":2849},{},[],{"type":27,"tag":28,"props":2851,"children":2852},{"id":434},[2853],{"type":33,"value":437},{"type":27,"tag":35,"props":2855,"children":2856},{},[2857],{"type":33,"value":442},{"type":27,"tag":185,"props":2859,"children":2860},{},[2861,2865,2869,2873],{"type":27,"tag":189,"props":2862,"children":2863},{},[2864],{"type":33,"value":450},{"type":27,"tag":189,"props":2866,"children":2867},{},[2868],{"type":33,"value":455},{"type":27,"tag":189,"props":2870,"children":2871},{},[2872],{"type":33,"value":460},{"type":27,"tag":189,"props":2874,"children":2875},{},[2876],{"type":33,"value":465},{"type":27,"tag":35,"props":2878,"children":2879},{},[2880],{"type":33,"value":470},{"type":27,"tag":378,"props":2882,"children":2883},{},[2884,2888,2892,2896],{"type":27,"tag":189,"props":2885,"children":2886},{},[2887],{"type":33,"value":478},{"type":27,"tag":189,"props":2889,"children":2890},{},[2891],{"type":33,"value":483},{"type":27,"tag":189,"props":2893,"children":2894},{},[2895],{"type":33,"value":488},{"type":27,"tag":189,"props":2897,"children":2898},{},[2899],{"type":33,"value":493},{"type":27,"tag":35,"props":2901,"children":2902},{},[2903],{"type":33,"value":498},{"type":27,"tag":46,"props":2905,"children":2906},{},[],{"type":27,"tag":28,"props":2908,"children":2909},{"id":504},[2910],{"type":33,"value":507},{"type":27,"tag":185,"props":2912,"children":2913},{},[2914,2918,2922,2926,2930,2934],{"type":27,"tag":189,"props":2915,"children":2916},{},[2917],{"type":33,"value":515},{"type":27,"tag":189,"props":2919,"children":2920},{},[2921],{"type":33,"value":520},{"type":27,"tag":189,"props":2923,"children":2924},{},[2925],{"type":33,"value":525},{"type":27,"tag":189,"props":2927,"children":2928},{},[2929],{"type":33,"value":530},{"type":27,"tag":189,"props":2931,"children":2932},{},[2933],{"type":33,"value":535},{"type":27,"tag":189,"props":2935,"children":2936},{},[2937],{"type":33,"value":540},{"type":27,"tag":46,"props":2939,"children":2940},{},[],{"type":27,"tag":28,"props":2942,"children":2943},{"id":546},[2944],{"type":33,"value":549},{"type":27,"tag":35,"props":2946,"children":2947},{},[2948],{"type":33,"value":554},{"type":27,"tag":35,"props":2950,"children":2951},{},[2952],{"type":33,"value":559},{"type":27,"tag":185,"props":2954,"children":2955},{},[2956,2963,2970],{"type":27,"tag":189,"props":2957,"children":2958},{},[2959],{"type":27,"tag":567,"props":2960,"children":2961},{"href":569},[2962],{"type":33,"value":572},{"type":27,"tag":189,"props":2964,"children":2965},{},[2966],{"type":27,"tag":567,"props":2967,"children":2968},{"href":578},[2969],{"type":33,"value":581},{"type":27,"tag":189,"props":2971,"children":2972},{},[2973],{"type":27,"tag":567,"props":2974,"children":2975},{"href":587},[2976],{"type":33,"value":590},{"title":7,"searchDepth":592,"depth":592,"links":2978},[2979,2980,2981,2982,2983,2984,2985,2986,2987,2988],{"id":30,"depth":595,"text":8},{"id":51,"depth":595,"text":54},{"id":175,"depth":595,"text":178},{"id":219,"depth":595,"text":222},{"id":274,"depth":595,"text":277},{"id":321,"depth":595,"text":324},{"id":368,"depth":595,"text":371},{"id":434,"depth":595,"text":437},{"id":504,"depth":595,"text":507},{"id":546,"depth":595,"text":549},1775659683894]