[{"data":1,"prerenderedAt":797},["ShallowReactive",2],{"content-/topics/engineering/code-splitting-dynamic-import-complete-guide":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"category":5,"tags":11,"author":17,"featured":18,"series":19,"seriesOrder":20,"readingTime":21,"image":22,"body":23,"_type":791,"_id":792,"_source":793,"_file":794,"_stem":795,"_extension":796},"/topics/engineering/code-splitting-dynamic-import-complete-guide","engineering",false,"","代码分割与动态导入完整方案：不是拆得越碎越快","包体优化最怕“看见大 bundle 就想砍”。本文从首屏预算、路由级拆分、组件级延迟加载、预取策略与过度分包的反作用入手，讲清代码分割真正的工程方法论。","2026-04-25",[12,13,14,15,16],"代码分割","动态导入","Bundle 优化","Vite","Web 性能","小明",true,"web-performance-extreme",4,14,"/images/articles/code-splitting-dynamic-import-complete-guide-cover.jpg",{"type":24,"children":25,"toc":768},"root",[26,34,40,49,54,84,95,100,104,111,116,121,139,144,167,172,180,183,189,196,201,206,314,319,325,330,348,404,409,415,420,425,428,434,439,444,449,467,472,475,481,487,492,497,510,516,521,526,531,534,540,545,550,568,573,578,586,589,595,601,606,612,617,623,628,633,651,654,660,689,694,697,703,708,713,718,721,726,749,754,762],{"type":27,"tag":28,"props":29,"children":31},"element","h1",{"id":30},"代码分割与动态导入完整方案不是拆得越碎越快",[32],{"type":33,"value":8},"text",{"type":27,"tag":35,"props":36,"children":37},"p",{},[38],{"type":33,"value":39},"很多人一看到包体分析图，第一反应就是：",{"type":27,"tag":41,"props":42,"children":43},"blockquote",{},[44],{"type":27,"tag":35,"props":45,"children":46},{},[47],{"type":33,"value":48},"这个太大了，拆。",{"type":27,"tag":35,"props":50,"children":51},{},[52],{"type":33,"value":53},"然后开始疯狂上动态导入，最终得到一个看似“被优化”过的项目：",{"type":27,"tag":55,"props":56,"children":57},"ul",{},[58,64,69,74,79],{"type":27,"tag":59,"props":60,"children":61},"li",{},[62],{"type":33,"value":63},"首屏 bundle 变小了",{"type":27,"tag":59,"props":65,"children":66},{},[67],{"type":33,"value":68},"但页面切换变得卡顿",{"type":27,"tag":59,"props":70,"children":71},{},[72],{"type":33,"value":73},"网络请求数量暴涨",{"type":27,"tag":59,"props":75,"children":76},{},[77],{"type":33,"value":78},"组件边界越来越复杂",{"type":27,"tag":59,"props":80,"children":81},{},[82],{"type":33,"value":83},"实际体验没变好，甚至更差",{"type":27,"tag":35,"props":85,"children":86},{},[87,89],{"type":33,"value":88},"原因很简单：",{"type":27,"tag":90,"props":91,"children":92},"strong",{},[93],{"type":33,"value":94},"代码分割不是拆文件，而是在管理等待。",{"type":27,"tag":35,"props":96,"children":97},{},[98],{"type":33,"value":99},"你拆得越多，等待被切成的段也越多。如果这些等待恰好落在用户最敏感的时候，那优化就会变成重新分配痛苦。",{"type":27,"tag":101,"props":102,"children":103},"hr",{},[],{"type":27,"tag":105,"props":106,"children":108},"h2",{"id":107},"_1-先明确什么代码应该待在首屏里",[109],{"type":33,"value":110},"1. 先明确：什么代码应该待在首屏里",{"type":27,"tag":35,"props":112,"children":113},{},[114],{"type":33,"value":115},"首屏预算的核心不是“尽量少”，而是“足够完成首屏任务”。",{"type":27,"tag":35,"props":117,"children":118},{},[119],{"type":33,"value":120},"可以留在首屏的代码通常满足这几个条件：",{"type":27,"tag":55,"props":122,"children":123},{},[124,129,134],{"type":27,"tag":59,"props":125,"children":126},{},[127],{"type":33,"value":128},"首屏一定会用到",{"type":27,"tag":59,"props":130,"children":131},{},[132],{"type":33,"value":133},"延后加载会直接影响首屏感知",{"type":27,"tag":59,"props":135,"children":136},{},[137],{"type":33,"value":138},"体积相对可控",{"type":27,"tag":35,"props":140,"children":141},{},[142],{"type":33,"value":143},"适合延后的代码通常是：",{"type":27,"tag":55,"props":145,"children":146},{},[147,152,157,162],{"type":27,"tag":59,"props":148,"children":149},{},[150],{"type":33,"value":151},"某些路由独有模块",{"type":27,"tag":59,"props":153,"children":154},{},[155],{"type":33,"value":156},"低频交互弹层",{"type":27,"tag":59,"props":158,"children":159},{},[160],{"type":33,"value":161},"管理后台重型图表",{"type":27,"tag":59,"props":163,"children":164},{},[165],{"type":33,"value":166},"富编辑器、地图、可视化面板",{"type":27,"tag":35,"props":168,"children":169},{},[170],{"type":33,"value":171},"一句话：",{"type":27,"tag":41,"props":173,"children":174},{},[175],{"type":27,"tag":35,"props":176,"children":177},{},[178],{"type":33,"value":179},"首屏只养“今天就要上场的人”，不要把全公司都带上台。",{"type":27,"tag":101,"props":181,"children":182},{},[],{"type":27,"tag":105,"props":184,"children":186},{"id":185},"_2-代码分割的三个层级",[187],{"type":33,"value":188},"2. 代码分割的三个层级",{"type":27,"tag":190,"props":191,"children":193},"h3",{"id":192},"_21-路由级分割收益最大也最稳",[194],{"type":33,"value":195},"2.1 路由级分割：收益最大，也最稳",{"type":27,"tag":35,"props":197,"children":198},{},[199],{"type":33,"value":200},"这是最优先做的。",{"type":27,"tag":35,"props":202,"children":203},{},[204],{"type":33,"value":205},"因为用户不可能同时打开所有页面，按路由拆分能直接避免无意义的预加载。",{"type":27,"tag":207,"props":208,"children":212},"pre",{"className":209,"code":210,"language":211,"meta":7,"style":7},"language-ts shiki shiki-themes github-dark","const UserSettingsPage = () => import('./pages/UserSettingsPage.vue')\nconst AdminDashboardPage = () => import('./pages/AdminDashboardPage.vue')\n","ts",[213],{"type":27,"tag":214,"props":215,"children":216},"code",{"__ignoreMap":7},[217,272],{"type":27,"tag":218,"props":219,"children":222},"span",{"class":220,"line":221},"line",1,[223,229,235,240,246,251,256,261,267],{"type":27,"tag":218,"props":224,"children":226},{"style":225},"--shiki-default:#F97583",[227],{"type":33,"value":228},"const",{"type":27,"tag":218,"props":230,"children":232},{"style":231},"--shiki-default:#B392F0",[233],{"type":33,"value":234}," UserSettingsPage",{"type":27,"tag":218,"props":236,"children":237},{"style":225},[238],{"type":33,"value":239}," =",{"type":27,"tag":218,"props":241,"children":243},{"style":242},"--shiki-default:#E1E4E8",[244],{"type":33,"value":245}," () ",{"type":27,"tag":218,"props":247,"children":248},{"style":225},[249],{"type":33,"value":250},"=>",{"type":27,"tag":218,"props":252,"children":253},{"style":225},[254],{"type":33,"value":255}," import",{"type":27,"tag":218,"props":257,"children":258},{"style":242},[259],{"type":33,"value":260},"(",{"type":27,"tag":218,"props":262,"children":264},{"style":263},"--shiki-default:#9ECBFF",[265],{"type":33,"value":266},"'./pages/UserSettingsPage.vue'",{"type":27,"tag":218,"props":268,"children":269},{"style":242},[270],{"type":33,"value":271},")\n",{"type":27,"tag":218,"props":273,"children":275},{"class":220,"line":274},2,[276,280,285,289,293,297,301,305,310],{"type":27,"tag":218,"props":277,"children":278},{"style":225},[279],{"type":33,"value":228},{"type":27,"tag":218,"props":281,"children":282},{"style":231},[283],{"type":33,"value":284}," AdminDashboardPage",{"type":27,"tag":218,"props":286,"children":287},{"style":225},[288],{"type":33,"value":239},{"type":27,"tag":218,"props":290,"children":291},{"style":242},[292],{"type":33,"value":245},{"type":27,"tag":218,"props":294,"children":295},{"style":225},[296],{"type":33,"value":250},{"type":27,"tag":218,"props":298,"children":299},{"style":225},[300],{"type":33,"value":255},{"type":27,"tag":218,"props":302,"children":303},{"style":242},[304],{"type":33,"value":260},{"type":27,"tag":218,"props":306,"children":307},{"style":263},[308],{"type":33,"value":309},"'./pages/AdminDashboardPage.vue'",{"type":27,"tag":218,"props":311,"children":312},{"style":242},[313],{"type":33,"value":271},{"type":27,"tag":35,"props":315,"children":316},{},[317],{"type":33,"value":318},"路由级拆分的好处在于业务边界天然清晰，团队也容易理解和维护。",{"type":27,"tag":190,"props":320,"children":322},{"id":321},"_22-组件级分割针对重型模块精准下刀",[323],{"type":33,"value":324},"2.2 组件级分割：针对重型模块精准下刀",{"type":27,"tag":35,"props":326,"children":327},{},[328],{"type":33,"value":329},"适用于：",{"type":27,"tag":55,"props":331,"children":332},{},[333,338,343],{"type":27,"tag":59,"props":334,"children":335},{},[336],{"type":33,"value":337},"只有用户展开时才需要的面板",{"type":27,"tag":59,"props":339,"children":340},{},[341],{"type":33,"value":342},"首屏不出现的复杂图表",{"type":27,"tag":59,"props":344,"children":345},{},[346],{"type":33,"value":347},"依赖大体积第三方库的模块",{"type":27,"tag":207,"props":349,"children":351},{"className":209,"code":350,"language":211,"meta":7,"style":7},"const ChartPanel = defineAsyncComponent(() => import('./ChartPanel.vue'))\n",[352],{"type":27,"tag":214,"props":353,"children":354},{"__ignoreMap":7},[355],{"type":27,"tag":218,"props":356,"children":357},{"class":220,"line":221},[358,362,368,372,377,382,386,390,394,399],{"type":27,"tag":218,"props":359,"children":360},{"style":225},[361],{"type":33,"value":228},{"type":27,"tag":218,"props":363,"children":365},{"style":364},"--shiki-default:#79B8FF",[366],{"type":33,"value":367}," ChartPanel",{"type":27,"tag":218,"props":369,"children":370},{"style":225},[371],{"type":33,"value":239},{"type":27,"tag":218,"props":373,"children":374},{"style":231},[375],{"type":33,"value":376}," defineAsyncComponent",{"type":27,"tag":218,"props":378,"children":379},{"style":242},[380],{"type":33,"value":381},"(() ",{"type":27,"tag":218,"props":383,"children":384},{"style":225},[385],{"type":33,"value":250},{"type":27,"tag":218,"props":387,"children":388},{"style":225},[389],{"type":33,"value":255},{"type":27,"tag":218,"props":391,"children":392},{"style":242},[393],{"type":33,"value":260},{"type":27,"tag":218,"props":395,"children":396},{"style":263},[397],{"type":33,"value":398},"'./ChartPanel.vue'",{"type":27,"tag":218,"props":400,"children":401},{"style":242},[402],{"type":33,"value":403},"))\n",{"type":27,"tag":35,"props":405,"children":406},{},[407],{"type":33,"value":408},"但要注意，组件级分割越多，页面状态切换的等待片段就越多。需要精挑对象，别把一堆普通组件都异步化。",{"type":27,"tag":190,"props":410,"children":412},{"id":411},"_23-库级分割谨慎对待大依赖",[413],{"type":33,"value":414},"2.3 库级分割：谨慎对待大依赖",{"type":27,"tag":35,"props":416,"children":417},{},[418],{"type":33,"value":419},"像编辑器、图表、地图这类库，往往值得单独拆。",{"type":27,"tag":35,"props":421,"children":422},{},[423],{"type":33,"value":424},"但如果某个库会在多个关键页面重复被用到，过度拆分也会增加总请求成本。这里要看真实访问路径，而不是凭感觉。",{"type":27,"tag":101,"props":426,"children":427},{},[],{"type":27,"tag":105,"props":429,"children":431},{"id":430},"_3-动态导入真正解决的是低概率需求别占高优先级资源",[432],{"type":33,"value":433},"3. 动态导入真正解决的，是“低概率需求别占高优先级资源”",{"type":27,"tag":35,"props":435,"children":436},{},[437],{"type":33,"value":438},"这句话很关键。",{"type":27,"tag":35,"props":440,"children":441},{},[442],{"type":33,"value":443},"动态导入不是为了显得高级，而是为了把低频功能从高优先级路径里移走。",{"type":27,"tag":35,"props":445,"children":446},{},[447],{"type":33,"value":448},"例如一个后台页面：",{"type":27,"tag":55,"props":450,"children":451},{},[452,457,462],{"type":27,"tag":59,"props":453,"children":454},{},[455],{"type":33,"value":456},"列表主体每次必看",{"type":27,"tag":59,"props":458,"children":459},{},[460],{"type":33,"value":461},"导出 Excel 每天可能点几次",{"type":27,"tag":59,"props":463,"children":464},{},[465],{"type":33,"value":466},"高级筛选弹窗不是每个用户都会开",{"type":27,"tag":35,"props":468,"children":469},{},[470],{"type":33,"value":471},"那导出模块和高级筛选，就不该压在首屏上。",{"type":27,"tag":101,"props":473,"children":474},{},[],{"type":27,"tag":105,"props":476,"children":478},{"id":477},"_4-预取和预加载别把未来可能会用错当成现在必须加载",[479],{"type":33,"value":480},"4. 预取和预加载：别把“未来可能会用”错当成“现在必须加载”",{"type":27,"tag":190,"props":482,"children":484},{"id":483},"_41-preload-是高优先级承诺",[485],{"type":33,"value":486},"4.1 preload 是高优先级承诺",{"type":27,"tag":35,"props":488,"children":489},{},[490],{"type":33,"value":491},"你告诉浏览器：这个资源很快就要用到，请优先安排。",{"type":27,"tag":35,"props":493,"children":494},{},[495],{"type":33,"value":496},"这适合：",{"type":27,"tag":55,"props":498,"children":499},{},[500,505],{"type":27,"tag":59,"props":501,"children":502},{},[503],{"type":33,"value":504},"下一帧就会显示的资源",{"type":27,"tag":59,"props":506,"children":507},{},[508],{"type":33,"value":509},"当前页面非常关键的后续依赖",{"type":27,"tag":190,"props":511,"children":513},{"id":512},"_42-prefetch-是低优先级押注",[514],{"type":33,"value":515},"4.2 prefetch 是低优先级押注",{"type":27,"tag":35,"props":517,"children":518},{},[519],{"type":33,"value":520},"你告诉浏览器：用户可能稍后会用到，有空再拉。",{"type":27,"tag":35,"props":522,"children":523},{},[524],{"type":33,"value":525},"这适合下一路由的预测性加载，但它不应该抢走当前页面的关键带宽。",{"type":27,"tag":35,"props":527,"children":528},{},[529],{"type":33,"value":530},"很多性能事故恰好来自把大量“也许会用”的代码，用 preload 提前运进来，结果首屏用户反而被挤慢。",{"type":27,"tag":101,"props":532,"children":533},{},[],{"type":27,"tag":105,"props":535,"children":537},{"id":536},"_5-一个更成熟的拆包原则按用户旅程拆不按目录拆",[538],{"type":33,"value":539},"5. 一个更成熟的拆包原则：按用户旅程拆，不按目录拆",{"type":27,"tag":35,"props":541,"children":542},{},[543],{"type":33,"value":544},"很多代码库按技术目录很好看，但不一定适合性能。",{"type":27,"tag":35,"props":546,"children":547},{},[548],{"type":33,"value":549},"例如：",{"type":27,"tag":55,"props":551,"children":552},{},[553,558,563],{"type":27,"tag":59,"props":554,"children":555},{},[556],{"type":33,"value":557},"从首页到商品详情是高概率路径",{"type":27,"tag":59,"props":559,"children":560},{},[561],{"type":33,"value":562},"从详情到支付是高价值路径",{"type":27,"tag":59,"props":564,"children":565},{},[566],{"type":33,"value":567},"从支付到投诉中心是低概率路径",{"type":27,"tag":35,"props":569,"children":570},{},[571],{"type":33,"value":572},"那你的拆包策略，就应该优先服务前两条路径。",{"type":27,"tag":35,"props":574,"children":575},{},[576],{"type":33,"value":577},"也就是说：",{"type":27,"tag":41,"props":579,"children":580},{},[581],{"type":27,"tag":35,"props":582,"children":583},{},[584],{"type":33,"value":585},"代码分割最应该听产品路径的话，而不是听文件夹话语权。",{"type":27,"tag":101,"props":587,"children":588},{},[],{"type":27,"tag":105,"props":590,"children":592},{"id":591},"_6-过度分包的副作用很多团队都吃过亏",[593],{"type":33,"value":594},"6. 过度分包的副作用，很多团队都吃过亏",{"type":27,"tag":190,"props":596,"children":598},{"id":597},"_61-请求数量暴增",[599],{"type":33,"value":600},"6.1 请求数量暴增",{"type":27,"tag":35,"props":602,"children":603},{},[604],{"type":33,"value":605},"HTTP/2 和 HTTP/3 确实改善了并发请求问题，但它们没有把“管理更多资源”变成零成本。",{"type":27,"tag":190,"props":607,"children":609},{"id":608},"_62-切换时抖动变多",[610],{"type":33,"value":611},"6.2 切换时抖动变多",{"type":27,"tag":35,"props":613,"children":614},{},[615],{"type":33,"value":616},"如果每个操作都要现拉一个小包，用户会感到界面像在“临时借零件”。",{"type":27,"tag":190,"props":618,"children":620},{"id":619},"_63-错误边界更复杂",[621],{"type":33,"value":622},"6.3 错误边界更复杂",{"type":27,"tag":35,"props":624,"children":625},{},[626],{"type":33,"value":627},"动态导入失败、弱网超时、缓存更新不一致，都会让用户看到更奇怪的问题。",{"type":27,"tag":35,"props":629,"children":630},{},[631],{"type":33,"value":632},"因此成熟方案不是“尽量多拆”，而是：",{"type":27,"tag":55,"props":634,"children":635},{},[636,641,646],{"type":27,"tag":59,"props":637,"children":638},{},[639],{"type":33,"value":640},"大块高频路径，提前组织好",{"type":27,"tag":59,"props":642,"children":643},{},[644],{"type":33,"value":645},"大而低频模块，延后加载",{"type":27,"tag":59,"props":647,"children":648},{},[649],{"type":33,"value":650},"中间地带，靠数据验证",{"type":27,"tag":101,"props":652,"children":653},{},[],{"type":27,"tag":105,"props":655,"children":657},{"id":656},"_7-一套够用的执行流程",[658],{"type":33,"value":659},"7. 一套够用的执行流程",{"type":27,"tag":661,"props":662,"children":663},"ol",{},[664,669,674,679,684],{"type":27,"tag":59,"props":665,"children":666},{},[667],{"type":33,"value":668},"先跑 bundle 分析，识别体积最大且非首屏必需的模块",{"type":27,"tag":59,"props":670,"children":671},{},[672],{"type":33,"value":673},"结合真实访问路径，找出高频与低频场景",{"type":27,"tag":59,"props":675,"children":676},{},[677],{"type":33,"value":678},"先做路由级拆分，再做组件级精准优化",{"type":27,"tag":59,"props":680,"children":681},{},[682],{"type":33,"value":683},"监控首屏体积、切页耗时、动态导入失败率",{"type":27,"tag":59,"props":685,"children":686},{},[687],{"type":33,"value":688},"避免一次性重构全部打包策略",{"type":27,"tag":35,"props":690,"children":691},{},[692],{"type":33,"value":693},"这个顺序的好处是：收益稳定，回滚容易。",{"type":27,"tag":101,"props":695,"children":696},{},[],{"type":27,"tag":105,"props":698,"children":700},{"id":699},"_8-一个朴素但常被忽略的事实",[701],{"type":33,"value":702},"8. 一个朴素但常被忽略的事实",{"type":27,"tag":35,"props":704,"children":705},{},[706],{"type":33,"value":707},"很多项目的性能问题，不是“不会分包”，而是业务边界模糊导致任何页面都像一锅大杂烩。",{"type":27,"tag":35,"props":709,"children":710},{},[711],{"type":33,"value":712},"换句话说，代码分割最后考验的不是打包器，而是架构抽象能力。",{"type":27,"tag":35,"props":714,"children":715},{},[716],{"type":33,"value":717},"如果你的页面模块之间互相强依赖、共享状态横飞，那任何拆分都会痛苦。因为包只是边界的投影。",{"type":27,"tag":101,"props":719,"children":720},{},[],{"type":27,"tag":105,"props":722,"children":724},{"id":723},"总结",[725],{"type":33,"value":723},{"type":27,"tag":55,"props":727,"children":728},{},[729,734,739,744],{"type":27,"tag":59,"props":730,"children":731},{},[732],{"type":33,"value":733},"代码分割不是拆得越碎越好，而是等待管理得越合理越好。",{"type":27,"tag":59,"props":735,"children":736},{},[737],{"type":33,"value":738},"优先做路由级拆分，再对重型低频模块做组件级延迟加载。",{"type":27,"tag":59,"props":740,"children":741},{},[742],{"type":33,"value":743},"预取和预加载要区分优先级，不要让未来需求抢占当前带宽。",{"type":27,"tag":59,"props":745,"children":746},{},[747],{"type":33,"value":748},"最终效果取决于业务边界与用户路径，而不只是构建工具配置。",{"type":27,"tag":35,"props":750,"children":751},{},[752],{"type":33,"value":753},"小明收尾一句：",{"type":27,"tag":41,"props":755,"children":756},{},[757],{"type":27,"tag":35,"props":758,"children":759},{},[760],{"type":33,"value":761},"好的拆包不是把系统切成碎片，而是让每一段等待都刚好发生在用户愿意等的时候。",{"type":27,"tag":763,"props":764,"children":765},"style",{},[766],{"type":33,"value":767},"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);}",{"title":7,"searchDepth":769,"depth":769,"links":770},3,[771,772,777,778,782,783,788,789,790],{"id":107,"depth":274,"text":110},{"id":185,"depth":274,"text":188,"children":773},[774,775,776],{"id":192,"depth":769,"text":195},{"id":321,"depth":769,"text":324},{"id":411,"depth":769,"text":414},{"id":430,"depth":274,"text":433},{"id":477,"depth":274,"text":480,"children":779},[780,781],{"id":483,"depth":769,"text":486},{"id":512,"depth":769,"text":515},{"id":536,"depth":274,"text":539},{"id":591,"depth":274,"text":594,"children":784},[785,786,787],{"id":597,"depth":769,"text":600},{"id":608,"depth":769,"text":611},{"id":619,"depth":769,"text":622},{"id":656,"depth":274,"text":659},{"id":699,"depth":274,"text":702},{"id":723,"depth":274,"text":723},"markdown","content:topics:engineering:code-splitting-dynamic-import-complete-guide.md","content","topics/engineering/code-splitting-dynamic-import-complete-guide.md","topics/engineering/code-splitting-dynamic-import-complete-guide","md",1777109940767]