JavaScript代码分割核心是动态import()语法,配合路由拆分、第三方库轻量化、预加载策略及打包分析优化,实现按需加载以减少首屏体积。
JavaScript 实现代码分割、按需
加载模块,核心是利用现代打包工具(如 Webpack、Vite)的动态 import() 语法,配合合理的路由或功能划分,把大体积的 JS 拆成小块,在真正需要时才加载,从而显著减少首屏资源体积和解析时间。
import() 动态导入替代静态 import
静态 import 会在构建时被提前打包进主 bundle,而 import() 是一个返回 Promise 的函数,Webpack/Vite 能自动识别并将其单独拆包:
import { utils } from './utils.js';
const { utils } = await import('./utils.js'); 或 import('./utils.js').then(module => {...})
注意:动态 import() 只支持在函数作用域内调用(不能在模块顶层直接写),适合放在事件回调、条件分支或组件挂载逻辑中。
单页应用中,不同路由对应不同视图,天然适合按路由拆分。以 React Router + Vite 为例:
const Home = () => import('@/pages/Home.vue');
{ path: '/', component: () => import('@/pages/Home.vue') }
element: + loader: () => import('./About').then(m => ({ Component: m.default }))
打包后每个页面会生成独立 chunk(如 Home.abc123.js),访问 / 时只加载首页代码,跳转到 /about 才拉取对应 chunk。
有些大型依赖(如 moment、lodash、monaco-editor)占体积大但可能只用其中一两个方法:
date-fns 替代 moment,用 lodash-es + 按需导入:import startOfToday from 'date-fns/startOfToday';
if (isEditing) await import('monaco-editor');
externals 或 CDN:将 jQuery、React 等稳定依赖外置,避免重复打包。在动态 import() 后添加魔法注释,可控制预加载策略:
import(/* webpackPrefetch: true */ './ReportModule.js'):空闲时预取(适合用户**很可能后续访问**的模块,如“导出报表”按钮后的模块)import(/* webpackPreload: true */ './Header.js'):与当前导航并行加载(适合**当前路由即将用到**的模块,如首屏 Header 中的搜索框逻辑)Vite 中对应的是 /* @vite-ignore */ + 插件支持,或直接用 import.meta.webpackContext(旧版);新版 Vite 更推荐用 import('./mod.js').then(...) + 构建配置中的 build.rollupOptions.output.manualChunks 精细分包。
不复杂但容易忽略:别只盯着 JS 拆分,记得检查拆出的 chunk 是否有公共依赖重复打包(用 webpack-bundle-analyzer 分析)、是否启用了 Gzip/Brotli 压缩、以及是否设置了 HTTP 缓存头。真实优化效果,得看 Lighthouse 的 “Reduce JavaScript payloads” 和 “Eliminate render-blocking resources” 两项指标。