核心是让首屏只加载几十KB,其余模块按需加载:通过动态import()拆分、路由驱动分包、提取公共依赖、配合preload/prefetch优化加载时机。

直接爆破5MB的JS巨石,核心不是“拆得越多越好”,而是让首屏只加载真正需要的那几十KB——其余模块在用户触发或即将进入时才拉取。关键靠动态导入 + 路由驱动拆分 + 构建工具自动分包,不是手动切文件。
用动态import()把功能模块从主包里“摘”出来
静态import会把模块提前打进main.js,哪怕它只在弹窗里用一次。换成动态导入,构建工具(Webpack/Vite)就能把它单独打包成一个chunk:
- 把非首屏逻辑封装成独立模块,比如
reportUtils.js、pdfExporter.js - 在用户点击按钮或打开面板时再加载:
button.addEventListener('click', () => import('./pdfExporter').then(m => m.export())) - 避免在循环或高频事件里反复调用import(),防止瞬间发起几十个请求
按路由拆分,让每个页面只加载自己的JS
单页应用天然适合这条路:访问首页不加载后台管理代码,点进个人中心才拉它的JS。框架配合构建工具能自动完成:
- Vue Router:
{ path: '/admin', component: () => import('@/views/Admin.vue') } - React Router v6.4+:
element: <Suspense fallback="..."><Outlet /></Suspense>+loader: () => import('./Admin').then(m => ({ Component: m.default })) - 每个路由组件生成独立chunk(如
admin.a1b2c3.js),首次访问/时完全不下载它
提取公共依赖,避免重复打包
5MB里大概有1.2MB是lodash、moment、chart.js这类被多个页面共用的库。不提取就会在每个路由chunk里重复出现:
- Webpack配置
optimization.splitChunks,设chunks: 'all'并指定minSize和maxInitialRequests - Vite中默认启用
build.rollupOptions.output.manualChunks,可按包名归类:vendor: ['lodash', 'axios'] - 给公共chunk加内容哈希名(如
vendor.e8f9d2.js),浏览器长期缓存,更新一个页面不影响其他chunk缓存命中
用preload/prefetch控制加载时机
拆完之后,别让用户体验到“点了跳转才开始下载”的卡顿:
- 对关键路由(如登录后必进的首页),用
<link rel="preload" href="home.xxx.js" as="script">提前拉取 - 对次要路由(如“帮助中心”),用
<link rel="prefetch" href="help.xxx.js">空闲时静默加载 - 结合用户行为预测——比如鼠标悬停在导航菜单项上超过300ms,就预取对应路由chunk



















