动态导入(import())是代码分包核心,需路径为字符串字面量、运行时调用、构建配置支持,并配合加载状态与预取策略提升体验。

动态导入(import())是实现 JavaScript 代码分包的核心手段,它本身不直接“分包”,而是向打包工具(如 Vite、Webpack)发出信号:此处模块应被单独打包为一个独立 chunk,并在运行时按需加载。设计是否有效,关键在于路径写法、调用时机、构建配置三者协同。
路径必须是字符串字面量,不能拼接变量
构建工具靠静态分析确定哪些模块可被拆出。一旦路径含变量或模板字符串(如 import(`./${name}.js`)),工具无法推断具体模块列表,就会退化为全量打包或忽略分割。
- ✅ 正确:
import('./charts.js')、import('./features/editor.js') - ❌ 错误:
import(`./${feature}.js`)、import('./' + modulePath) - ⚠️ 可接受的条件分支:
const path = isPro ? './pro.js' : './free.js'; import(path);—— 因为两个路径都明确、可枚举
调用必须在运行时上下文中,不能在顶层
import() 是函数调用,不是声明语法,必须包裹在函数、事件处理器、生命周期钩子或条件块中。
- ✅ 合理位置:按钮点击回调、路由切换后、组件
useEffect或mounted钩子内、if (user.hasPermission)分支中 - ❌ 禁止写法:模块文件最外层直接写
import('./utils.js')→ 会报SyntaxError: Unexpected token 'import' - ? 提示:Vue 单文件组件中推荐写成
defineAsyncComponent(() => import('./Heavy.vue'));React 中统一用React.lazy(() => import('./Page'))
配合构建工具启用自动分包与缓存策略
即使写了正确的 import(),若构建配置未开启或限制过严,仍不会生成新 chunk。
- Vite 默认支持,无需额外配置;如需自定义分包逻辑,可设置
build.rollupOptions.output.manualChunks - Webpack 需确保
optimization.splitChunks.chunks === 'all',且未通过cacheGroups错误地将动态导入模块强制合并进 vendor - 务必启用内容哈希(如
filename: '[name].[contenthash:8].js'),让浏览器对每个 chunk 单独缓存,更新时不影响其他模块 - 避免重复引用:同一模块若已被静态
import过,动态调用只会命中缓存,不会产生新 chunk —— 所以非首屏功能尽量只用动态导入接入
结合加载状态与预取策略提升体验
分包只是第一步,用户感知取决于加载过程是否平滑、资源是否预判到位。
- UI 必须反馈加载状态:React 用
<suspense fallback=""></suspense>,Vue 用v-if="!loaded"+ loading 插槽 - 对低优先级但大概率会访问的模块(如“帮助中心”),加 Webpack 魔法注释:
import(/* webpackPrefetch: true */ './help.js'),浏览器空闲时预取 - 对关键路径模块(如登录后立即需要的仪表盘图表),用
/* webpackPreload: true */提前加载,避免白屏等待 - 验证效果:打开 Chrome DevTools → Network → JS 标签页,观察是否出现多个小体积的
xxx.xxx.js;Lighthouse 报告中的 “Total Blocking Time” 和 “First Contentful Paint” 应有明显下降


















