[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild":1032,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild":1052},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":15,"pinned":16,"draft":16,"body":17,"seo":1028,"stem":1029,"id":1030,"extension":1031},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild","构建与分包","通过代码分割与构建配置减小首屏 JavaScript 体积。涵盖 React.lazy\u002FSuspense、路由级分割、Tree Shaking、Vite 生产构建、体积分析、最佳实践与常见坑。运行时优化见 runtime。","技能小册",[12,13,14],"react","performance","build","2025-07-23",false,{"type":18,"value":19,"toc":1014},"minimark",[20,23,33,37,53,57,135,141,144,156,215,218,222,308,311,315,326,382,465,480,484,608,614,617,640,739,750,753,773,776,779,816,819,905,908,930,933,936,1010],[21,22,8],"h1",{"id":8},[24,25,26,27,32],"p",{},"通过代码分割与构建配置减小首屏 JavaScript 体积。运行时优化见 ",[28,29,31],"a",{"href":30},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","runtime","。",[34,35,36],"h2",{"id":36},"核心概念",[24,38,39,40,44,45,48,49,52],{},"「构建优化」和「运行时优化」解决的是不同阶段的问题：构建优化关心",[41,42,43],"strong",{},"用户第一次打开页面要下载多少 JS\u002FCSS、多久能看到内容","；运行时优化关心",[41,46,47],{},"页面已经加载完成后，交互是否流畅","。两者常被混为一谈，但优化手段完全不同——构建侧的核心武器是",[41,50,51],{},"代码分割","：把不是「首屏必须」的代码拆成独立文件，延迟到真正需要时才加载。",[34,54,56],{"id":55},"reactlazy-与-suspense","React.lazy 与 Suspense",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","import { lazy, Suspense } from \"react\";\n\nconst Chart = lazy(() => import(\".\u002FChart\"));\n\nfunction Page() {\n  return (\n    \u003CSuspense fallback={\u003Cp>图表加载中…\u003C\u002Fp>}>\n      \u003CChart \u002F>\n    \u003C\u002FSuspense>\n  );\n}\n","tsx","",[65,66,67,75,82,88,93,99,105,111,117,123,129],"code",{"__ignoreMap":63},[68,69,72],"span",{"class":70,"line":71},"line",1,[68,73,74],{},"import { lazy, Suspense } from \"react\";\n",[68,76,78],{"class":70,"line":77},2,[68,79,81],{"emptyLinePlaceholder":80},true,"\n",[68,83,85],{"class":70,"line":84},3,[68,86,87],{},"const Chart = lazy(() => import(\".\u002FChart\"));\n",[68,89,91],{"class":70,"line":90},4,[68,92,81],{"emptyLinePlaceholder":80},[68,94,96],{"class":70,"line":95},5,[68,97,98],{},"function Page() {\n",[68,100,102],{"class":70,"line":101},6,[68,103,104],{},"  return (\n",[68,106,108],{"class":70,"line":107},7,[68,109,110],{},"    \u003CSuspense fallback={\u003Cp>图表加载中…\u003C\u002Fp>}>\n",[68,112,114],{"class":70,"line":113},8,[68,115,116],{},"      \u003CChart \u002F>\n",[68,118,120],{"class":70,"line":119},9,[68,121,122],{},"    \u003C\u002FSuspense>\n",[68,124,126],{"class":70,"line":125},10,[68,127,128],{},"  );\n",[68,130,132],{"class":70,"line":131},11,[68,133,134],{},"}\n",[24,136,137,140],{},[65,138,139],{},"import()"," 由打包工具（Vite、Webpack）拆成独立 chunk，访问路由或条件满足时再加载。",[34,142,143],{"id":143},"路由级分割",[24,145,146,147,151,152,155],{},"在 ",[28,148,150],{"href":149},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter","React Router"," 配置中对页面组件使用 ",[65,153,154],{},"lazy","，是 SPA 最常见的分割方式：",[58,157,159],{"className":60,"code":158,"language":62,"meta":63,"style":63},"import { createBrowserRouter } from \"react-router-dom\";\n\nconst router = createBrowserRouter([\n  {\n    path: \"\u002Fdashboard\",\n    lazy: async () => {\n      const { DashboardPage } = await import(\".\u002Fpages\u002FDashboardPage\");\n      return { Component: DashboardPage };\n    },\n  },\n]);\n",[65,160,161,166,170,175,180,185,190,195,200,205,210],{"__ignoreMap":63},[68,162,163],{"class":70,"line":71},[68,164,165],{},"import { createBrowserRouter } from \"react-router-dom\";\n",[68,167,168],{"class":70,"line":77},[68,169,81],{"emptyLinePlaceholder":80},[68,171,172],{"class":70,"line":84},[68,173,174],{},"const router = createBrowserRouter([\n",[68,176,177],{"class":70,"line":90},[68,178,179],{},"  {\n",[68,181,182],{"class":70,"line":95},[68,183,184],{},"    path: \"\u002Fdashboard\",\n",[68,186,187],{"class":70,"line":101},[68,188,189],{},"    lazy: async () => {\n",[68,191,192],{"class":70,"line":107},[68,193,194],{},"      const { DashboardPage } = await import(\".\u002Fpages\u002FDashboardPage\");\n",[68,196,197],{"class":70,"line":113},[68,198,199],{},"      return { Component: DashboardPage };\n",[68,201,202],{"class":70,"line":119},[68,203,204],{},"    },\n",[68,206,207],{"class":70,"line":125},[68,208,209],{},"  },\n",[68,211,212],{"class":70,"line":131},[68,213,214],{},"]);\n",[24,216,217],{},"每个路由对应的页面代码只在用户访问该路由时才下载，首屏只需加载入口路由的代码。",[34,219,221],{"id":220},"组件级分割按交互延迟加载","组件级分割：按交互延迟加载",[58,223,225],{"className":60,"code":224,"language":62,"meta":63,"style":63},"\u002F\u002F 富文本编辑器体积通常很大，仅在用户点击\"编辑\"后才需要\nconst RichTextEditor = lazy(() => import(\".\u002FRichTextEditor\"));\n\nfunction ArticleDetail() {\n  const [editing, setEditing] = useState(false);\n  return (\n    \u003Cdiv>\n      \u003Cbutton onClick={() => setEditing(true)}>编辑\u003C\u002Fbutton>\n      {editing && (\n        \u003CSuspense fallback={\u003CSpinner \u002F>}>\n          \u003CRichTextEditor \u002F>\n        \u003C\u002FSuspense>\n      )}\n    \u003C\u002Fdiv>\n  );\n}\n",[65,226,227,232,237,241,246,251,255,260,265,270,275,280,286,292,298,303],{"__ignoreMap":63},[68,228,229],{"class":70,"line":71},[68,230,231],{},"\u002F\u002F 富文本编辑器体积通常很大，仅在用户点击\"编辑\"后才需要\n",[68,233,234],{"class":70,"line":77},[68,235,236],{},"const RichTextEditor = lazy(() => import(\".\u002FRichTextEditor\"));\n",[68,238,239],{"class":70,"line":84},[68,240,81],{"emptyLinePlaceholder":80},[68,242,243],{"class":70,"line":90},[68,244,245],{},"function ArticleDetail() {\n",[68,247,248],{"class":70,"line":95},[68,249,250],{},"  const [editing, setEditing] = useState(false);\n",[68,252,253],{"class":70,"line":101},[68,254,104],{},[68,256,257],{"class":70,"line":107},[68,258,259],{},"    \u003Cdiv>\n",[68,261,262],{"class":70,"line":113},[68,263,264],{},"      \u003Cbutton onClick={() => setEditing(true)}>编辑\u003C\u002Fbutton>\n",[68,266,267],{"class":70,"line":119},[68,268,269],{},"      {editing && (\n",[68,271,272],{"class":70,"line":125},[68,273,274],{},"        \u003CSuspense fallback={\u003CSpinner \u002F>}>\n",[68,276,277],{"class":70,"line":131},[68,278,279],{},"          \u003CRichTextEditor \u002F>\n",[68,281,283],{"class":70,"line":282},12,[68,284,285],{},"        \u003C\u002FSuspense>\n",[68,287,289],{"class":70,"line":288},13,[68,290,291],{},"      )}\n",[68,293,295],{"class":70,"line":294},14,[68,296,297],{},"    \u003C\u002Fdiv>\n",[68,299,301],{"class":70,"line":300},15,[68,302,128],{},[68,304,306],{"class":70,"line":305},16,[68,307,134],{},[24,309,310],{},"对话框、图表、富文本编辑器、地图等「体积大但不总是需要」的组件，都适合用这种「按需触发」的懒加载模式，而不仅限于路由级别。",[34,312,314],{"id":313},"tree-shaking","Tree Shaking",[24,316,317,318,321,322,325],{},"使用 ",[41,319,320],{},"ESM"," 导入，生产构建开启压缩；避免 ",[65,323,324],{},"import entire library","：",[58,327,331],{"className":328,"code":329,"language":330,"meta":63,"style":63},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F 较好：按需\nimport { debounce } from \"lodash-es\";\n\n\u002F\u002F 注意：部分库需查文档是否支持 tree-shaking\n","ts",[65,332,333,339,373,377],{"__ignoreMap":63},[68,334,335],{"class":70,"line":71},[68,336,338],{"class":337},"sutJx","\u002F\u002F 较好：按需\n",[68,340,341,345,349,353,356,359,363,367,370],{"class":70,"line":77},[68,342,344],{"class":343},"sVHd0","import",[68,346,348],{"class":347},"sP7_E"," {",[68,350,352],{"class":351},"su5hD"," debounce",[68,354,355],{"class":347}," }",[68,357,358],{"class":343}," from",[68,360,362],{"class":361},"sjJ54"," \"",[68,364,366],{"class":365},"s_sjI","lodash-es",[68,368,369],{"class":361},"\"",[68,371,372],{"class":347},";\n",[68,374,375],{"class":70,"line":84},[68,376,81],{"emptyLinePlaceholder":80},[68,378,379],{"class":70,"line":90},[68,380,381],{"class":337},"\u002F\u002F 注意：部分库需查文档是否支持 tree-shaking\n",[58,383,385],{"className":328,"code":384,"language":330,"meta":63,"style":63},"\u002F\u002F 反例：整包导入，即使只用一个函数也会打入全部代码（若库不支持良好的 tree-shaking）\nimport _ from \"lodash\";\n_.debounce(fn, 300);\n\n\u002F\u002F 更好：明确从 ESM 版本按需导入\nimport debounce from \"lodash-es\u002Fdebounce\";\n",[65,386,387,392,411,438,442,447],{"__ignoreMap":63},[68,388,389],{"class":70,"line":71},[68,390,391],{"class":337},"\u002F\u002F 反例：整包导入，即使只用一个函数也会打入全部代码（若库不支持良好的 tree-shaking）\n",[68,393,394,396,399,402,404,407,409],{"class":70,"line":77},[68,395,344],{"class":343},[68,397,398],{"class":351}," _ ",[68,400,401],{"class":343},"from",[68,403,362],{"class":361},[68,405,406],{"class":365},"lodash",[68,408,369],{"class":361},[68,410,372],{"class":347},[68,412,413,416,419,423,426,429,433,436],{"class":70,"line":84},[68,414,415],{"class":351},"_",[68,417,418],{"class":347},".",[68,420,422],{"class":421},"sGLFI","debounce",[68,424,425],{"class":351},"(fn",[68,427,428],{"class":347},",",[68,430,432],{"class":431},"srdBf"," 300",[68,434,435],{"class":351},")",[68,437,372],{"class":347},[68,439,440],{"class":70,"line":90},[68,441,81],{"emptyLinePlaceholder":80},[68,443,444],{"class":70,"line":95},[68,445,446],{"class":337},"\u002F\u002F 更好：明确从 ESM 版本按需导入\n",[68,448,449,451,454,456,458,461,463],{"class":70,"line":101},[68,450,344],{"class":343},[68,452,453],{"class":351}," debounce ",[68,455,401],{"class":343},[68,457,362],{"class":361},[68,459,460],{"class":365},"lodash-es\u002Fdebounce",[68,462,369],{"class":361},[68,464,372],{"class":347},[24,466,467,468,471,472,475,476,479],{},"判断一个库是否支持良好的 tree-shaking，可查看其 ",[65,469,470],{},"package.json"," 是否有 ",[65,473,474],{},"\"sideEffects\": false"," 声明，以及是否提供 ESM 格式的产物（",[65,477,478],{},"module"," 字段）。",[34,481,483],{"id":482},"vite-生产构建","Vite 生产构建",[58,485,487],{"className":328,"code":486,"language":330,"meta":63,"style":63},"\u002F\u002F vite.config.ts 片段\nexport default defineConfig({\n  build: {\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          vendor: [\"react\", \"react-dom\"],\n        },\n      },\n    },\n  },\n});\n",[65,488,489,494,511,523,532,541,550,581,586,591,595,599],{"__ignoreMap":63},[68,490,491],{"class":70,"line":71},[68,492,493],{"class":337},"\u002F\u002F vite.config.ts 片段\n",[68,495,496,499,502,505,508],{"class":70,"line":77},[68,497,498],{"class":343},"export",[68,500,501],{"class":343}," default",[68,503,504],{"class":421}," defineConfig",[68,506,507],{"class":351},"(",[68,509,510],{"class":347},"{\n",[68,512,513,517,520],{"class":70,"line":84},[68,514,516],{"class":515},"skxfh","  build",[68,518,519],{"class":347},":",[68,521,522],{"class":347}," {\n",[68,524,525,528,530],{"class":70,"line":90},[68,526,527],{"class":515},"    rollupOptions",[68,529,519],{"class":347},[68,531,522],{"class":347},[68,533,534,537,539],{"class":70,"line":95},[68,535,536],{"class":515},"      output",[68,538,519],{"class":347},[68,540,522],{"class":347},[68,542,543,546,548],{"class":70,"line":101},[68,544,545],{"class":515},"        manualChunks",[68,547,519],{"class":347},[68,549,522],{"class":347},[68,551,552,555,557,560,562,564,566,568,570,573,575,578],{"class":70,"line":107},[68,553,554],{"class":515},"          vendor",[68,556,519],{"class":347},[68,558,559],{"class":351}," [",[68,561,369],{"class":361},[68,563,12],{"class":365},[68,565,369],{"class":361},[68,567,428],{"class":347},[68,569,362],{"class":361},[68,571,572],{"class":365},"react-dom",[68,574,369],{"class":361},[68,576,577],{"class":351},"]",[68,579,580],{"class":347},",\n",[68,582,583],{"class":70,"line":113},[68,584,585],{"class":347},"        },\n",[68,587,588],{"class":70,"line":119},[68,589,590],{"class":347},"      },\n",[68,592,593],{"class":70,"line":125},[68,594,204],{"class":347},[68,596,597],{"class":70,"line":131},[68,598,209],{"class":347},[68,600,601,604,606],{"class":70,"line":282},[68,602,603],{"class":347},"}",[68,605,435],{"class":351},[68,607,372],{"class":347},[24,609,610,613],{},[65,611,612],{},"manualChunks"," 将 React 等稳定依赖单独打包，利于长期缓存（具体策略按项目体积分析调整）。",[34,615,616],{"id":616},"分析构建产物",[58,618,622],{"className":619,"code":620,"language":621,"meta":63,"style":63},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm add -D rollup-plugin-visualizer\n","bash",[65,623,624],{"__ignoreMap":63},[68,625,626,630,633,637],{"class":70,"line":71},[68,627,629],{"class":628},"sbgvK","pnpm",[68,631,632],{"class":365}," add",[68,634,636],{"class":635},"stzsN"," -D",[68,638,639],{"class":365}," rollup-plugin-visualizer\n",[58,641,643],{"className":328,"code":642,"language":330,"meta":63,"style":63},"import { visualizer } from \"rollup-plugin-visualizer\";\n\nexport default defineConfig({\n  plugins: [react(), visualizer({ open: true, gzipSize: true })],\n});\n",[65,644,645,667,671,683,731],{"__ignoreMap":63},[68,646,647,649,651,654,656,658,660,663,665],{"class":70,"line":71},[68,648,344],{"class":343},[68,650,348],{"class":347},[68,652,653],{"class":351}," visualizer",[68,655,355],{"class":347},[68,657,358],{"class":343},[68,659,362],{"class":361},[68,661,662],{"class":365},"rollup-plugin-visualizer",[68,664,369],{"class":361},[68,666,372],{"class":347},[68,668,669],{"class":70,"line":77},[68,670,81],{"emptyLinePlaceholder":80},[68,672,673,675,677,679,681],{"class":70,"line":84},[68,674,498],{"class":343},[68,676,501],{"class":343},[68,678,504],{"class":421},[68,680,507],{"class":351},[68,682,510],{"class":347},[68,684,685,688,690,692,694,697,699,701,703,706,709,711,715,717,720,722,724,726,729],{"class":70,"line":90},[68,686,687],{"class":515},"  plugins",[68,689,519],{"class":347},[68,691,559],{"class":351},[68,693,12],{"class":421},[68,695,696],{"class":351},"()",[68,698,428],{"class":347},[68,700,653],{"class":421},[68,702,507],{"class":351},[68,704,705],{"class":347},"{",[68,707,708],{"class":515}," open",[68,710,519],{"class":347},[68,712,714],{"class":713},"syTEX"," true",[68,716,428],{"class":347},[68,718,719],{"class":515}," gzipSize",[68,721,519],{"class":347},[68,723,714],{"class":713},[68,725,355],{"class":347},[68,727,728],{"class":351},")]",[68,730,580],{"class":347},[68,732,733,735,737],{"class":70,"line":95},[68,734,603],{"class":347},[68,736,435],{"class":351},[68,738,372],{"class":347},[24,740,741,742,745,746,749],{},"构建后自动打开一份",[41,743,744],{},"可视化的 bundle 分析报告","，用矩形面积直观展示各模块占比，快速定位「哪个依赖占用了大量体积」。常见发现包括：误打入整个 UI 库、日期库（如 ",[65,747,748],{},"moment"," 完整语言包）、重复打包的多份相同依赖。",[34,751,752],{"id":752},"图片与静态资源优化",[58,754,756],{"className":60,"code":755,"language":62,"meta":63,"style":63},"\u002F\u002F Vite 支持在导入时进行部分转换\u002F内联小文件\nimport logo from \".\u002Flogo.svg?url\";\nimport iconRaw from \".\u002Ficon.svg?raw\";\n",[65,757,758,763,768],{"__ignoreMap":63},[68,759,760],{"class":70,"line":71},[68,761,762],{},"\u002F\u002F Vite 支持在导入时进行部分转换\u002F内联小文件\n",[68,764,765],{"class":70,"line":77},[68,766,767],{},"import logo from \".\u002Flogo.svg?url\";\n",[68,769,770],{"class":70,"line":84},[68,771,772],{},"import iconRaw from \".\u002Ficon.svg?raw\";\n",[24,774,775],{},"大图片建议转 WebP\u002FAVIF 并结合 CDN 处理裁剪与压缩，而不是把原图直接打进构建产物；小于阈值（默认 4KB）的资源 Vite 会自动 base64 内联，减少请求数但增加 JS\u002FCSS 体积，需要权衡。",[34,777,778],{"id":778},"最佳实践",[780,781,782,786,789,792,801],"ul",{},[783,784,785],"li",{},"路由级懒加载是「投入产出比最高」的第一步优化，几乎所有 SPA 都该做。",[783,787,788],{},"大体积、非首屏必需的组件（富文本、图表、地图）按交互时机懒加载，不要一次性打进首屏 bundle。",[783,790,791],{},"定期跑一次 bundle 分析（可作为 CI 步骤，体积超过阈值时告警），而不是等用户反馈\"加载慢\"才排查。",[783,793,794,796,797,800],{},[65,795,612],{}," 只对",[41,798,799],{},"变化频率低","的依赖（React、UI 库）有意义，业务代码频繁改动，拆多了反而增加请求数。",[783,802,803,804,807,808,810,811,807,813,815],{},"优先选择原生支持 ESM\u002Ftree-shaking 的库（",[65,805,806],{},"date-fns"," 优于 ",[65,809,748],{},"，",[65,812,366],{},[65,814,406],{},"）。",[34,817,818],{"id":818},"常见坑",[820,821,822,838],"table",{},[823,824,825],"thead",{},[826,827,828,832,835],"tr",{},[829,830,831],"th",{},"现象",[829,833,834],{},"常见原因",[829,836,837],{},"处理",[839,840,841,859,870,883,894],"tbody",{},[826,842,843,847,850],{},[844,845,846],"td",{},"首屏 JS 体积异常大",[844,848,849],{},"未做路由级分割，所有页面代码打进一个 bundle",[844,851,852,853,855,856],{},"对页面组件使用 ",[65,854,154],{}," + ",[65,857,858],{},"Suspense",[826,860,861,864,867],{},[844,862,863],{},"bundle 分析发现某个库占比异常高",[844,865,866],{},"整包导入了只用到一小部分功能的库",[844,868,869],{},"改为按需导入，或换更轻量的替代库",[826,871,872,877,880],{},[844,873,874,876],{},[65,875,612],{}," 配置后加载变慢",[844,878,879],{},"拆分粒度过细，导致请求数暴增",[844,881,882],{},"收敛拆分策略，只拆真正稳定且体积大的依赖",[826,884,885,888,891],{},[844,886,887],{},"图片资源体积过大拖慢首屏",[844,889,890],{},"直接打包原始大图，未压缩\u002F转格式",[844,892,893],{},"使用 WebP\u002FAVIF、CDN 处理、按需加载",[826,895,896,899,902],{},[844,897,898],{},"Suspense fallback 频繁闪烁",[844,900,901],{},"懒加载的 chunk 体积小但网络延迟高，加载时间短却仍触发 fallback",[844,903,904],{},"评估是否需要懒加载该组件，或添加最短展示时长",[34,906,907],{"id":907},"延伸阅读",[780,909,910,916,923],{},[783,911,912,915],{},[28,913,914],{"href":30},"运行时优化","：加载完成后的交互性能",[783,917,918,922],{},[28,919,921],{"href":920},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","：Suspense 的渲染机制",[783,924,925,929],{},[28,926,928],{"href":927},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","：Vite 项目结构与部署",[34,931,932],{"id":932},"参考文献",[24,934,935],{},"以下链接在编写时均可正常访问：",[820,937,938,948],{},[823,939,940],{},[826,941,942,945],{},[829,943,944],{},"资料",[829,946,947],{},"说明",[839,949,950,963,975,987,998],{},[826,951,952,960],{},[844,953,954],{},[28,955,959],{"href":956,"rel":957},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002Flazy",[958],"nofollow","React：lazy",[844,961,962],{},"懒加载",[826,964,965,972],{},[844,966,967],{},[28,968,971],{"href":969,"rel":970},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FSuspense",[958],"React：Suspense",[844,973,974],{},"边界",[826,976,977,984],{},[844,978,979],{},[28,980,983],{"href":981,"rel":982},"https:\u002F\u002Fcn.vite.dev\u002Fguide\u002Fbuild",[958],"Vite：构建生产版本",[844,985,986],{},"生产构建",[826,988,989,995],{},[844,990,991],{},[28,992,662],{"href":993,"rel":994},"https:\u002F\u002Fgithub.com\u002Fbtd\u002Frollup-plugin-visualizer",[958],[844,996,997],{},"构建体积分析",[826,999,1000,1007],{},[844,1001,1002],{},[28,1003,1006],{"href":1004,"rel":1005},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fcode-splitting-suspense",[958],"web.dev：Code splitting",[844,1008,1009],{},"代码分割实践",[1011,1012,1013],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":77,"depth":77,"links":1015},[1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027],{"id":36,"depth":77,"text":36},{"id":55,"depth":77,"text":56},{"id":143,"depth":77,"text":143},{"id":220,"depth":77,"text":221},{"id":313,"depth":77,"text":314},{"id":482,"depth":77,"text":483},{"id":616,"depth":77,"text":616},{"id":752,"depth":77,"text":752},{"id":778,"depth":77,"text":778},{"id":818,"depth":77,"text":818},{"id":907,"depth":77,"text":907},{"id":932,"depth":77,"text":932},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fperformance\u002Fbuild","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fperformance\u002Fbuild.md","md",{"items":1033},[1034,1036,1040,1042,1044,1048],{"path":30,"title":914,"description":1035,"category":10,"score":119},"减少不必要的组件重渲染与昂贵计算。涵盖重渲染成因、memo\u002FuseMemo\u002FuseCallback、虚拟滚动、Web Worker、Profiler 分析、最佳实践与常见坑。构建侧见 build。",{"path":1037,"title":1038,"description":1039,"category":10,"score":107},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fbuild","编译与打包优化","构建阶段的代码分割、Tree Shaking 与 SFC 编译优化。运行时见 runtime。",{"path":920,"title":921,"description":1041,"category":10,"score":90},"React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。",{"path":927,"title":928,"description":1043,"category":10,"score":90},"新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。",{"path":1045,"title":1046,"description":1047,"category":10,"score":90},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftypescript","TypeScript","React 官方推荐 TypeScript。类型主要落在 Props、事件、Ref 与 Hooks 返回值上。见 组件。",{"path":1049,"title":1050,"description":1051,"category":10,"score":90},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"series":1053,"title":13,"items":1054,"index":1058,"prev":1059,"next":1060},"booklet\u002Fframe\u002Freact\u002Fperformance",[1055,1056],{"path":7,"title":8,"date":15,"pinned":16},{"path":30,"title":914,"date":1057,"pinned":16},"2020-02-05",0,null,{"path":30,"title":914,"date":1057,"pinned":16}]