vue项目中,静态导入 vs 动态导入 详解

静态导入 vs 动态导入 详解

环境:Vite + Vue 2 + Rollup

时间:2026-07-10


一、基本写法

静态导入(Static Import)

js 复制代码
import Foo from './Foo.vue'

// 或具名导入
import { Button, Input } from 'element-ui'
  • 编译时执行,在模块初始化前就完成
  • 只能放在文件顶层,不能放在条件或函数中

动态导入(Dynamic Import)

js 复制代码
const Foo = () => import('./Foo.vue')

// 带路径变量的动态导入
const compName = 'TableList'
const Comp = () => import(`@/components/${compName}/index.vue`)
  • 运行时才执行,返回 Promise
  • 可以放在任意位置,可条件加载

二、打包产物差异

场景 1:同一组件被多处引用

假设三个页面都用到了 BigTable.vue(200KB)。

静态导入方式:

js 复制代码
// ─── PageA.vue ───
import BigTable from '@/components/BigTable.vue'

// ─── PageB.vue ───
import BigTable from '@/components/BigTable.vue'

// ─── PageC.vue ───
import BigTable from '@/components/BigTable.vue'
复制代码
打包结果:

PageA.\[hash].js    ← 内含 BigTable 代码(200KB)
PageB.\[hash].js    ← 内含 BigTable 代码(200KB)
PageC.\[hash].js    ← 内含 BigTable 代码(200KB)
                      \_\_\_\_\_
                      合计多出 400KB 重复体积

动态导入方式:

js 复制代码
// ─── PageA.vue ───
components: { BigTable: () => import('@/components/BigTable.vue') }

// ─── PageB.vue ───
components: { BigTable: () => import('@/components/BigTable.vue') }

// ─── PageC.vue ───
components: { BigTable: () => import('@/components/BigTable.vue') }
复制代码
打包结果:

BigTable.\[hash].js  ← 唯一 1 份(200KB)
PageA.\[hash].js     ← 不含 BigTable
PageB.\[hash].js     ← 不含 BigTable
PageC.\[hash].js     ← 不含 BigTable

**结论:同一组件被 N 处引用,静态导入打包 N 份,动态导入打包 1 份。**


场景 2:已被全局静态导入过

js 复制代码
// ─── global.js ───
import dialogAddEdit from '@/components/form/addEdit/index.vue'
Vue.component('DialogAddEdit', dialogAddEdit)

此时 dialogAddEdit 已被打包进主入口 chunk(app.\[hash].js)。

其他组件中再写动态导入:

js 复制代码
// ─── DialogFieldPower/index.vue ───
components: {
  // 动态导入------但组件已存在于主 chunk
  dialogAddEdit: () => import('@/components/form/addEdit')
}
复制代码
打包结果:

app.\[hash].js        ← 已有 dialogAddEdit 代码

// 上面的动态导入不会产生独立 chunk
// 编译后等价于:
// dialogAddEdit: () => Promise.resolve(app chunk 中已存在的模块)

**结论:组件已被全局静态导入过的,各处写的动态导入零额外体积。**


场景 3:组件只被一处使用

js 复制代码
// ─── LoginPage.vue ───
import HeavyChartLib from '@/components/HeavyChartLib.vue'  // 500KB
复制代码
打包结果:

LoginPage.\[hash].js  ← 内含 HeavyChartLib(500KB),只有 LoginPage 用

如果改成动态:

js 复制代码
// ─── LoginPage.vue ───
components: {
  HeavyChartLib: () => import('@/components/HeavyChartLib.vue')
}
复制代码
打包结果:

HeavyChartLib.\[hash].js  ← 独立 chunk(500KB)
LoginPage.\[hash].js      ← 不含 HeavyChartLib,体积更小

**结论:只在一处使用的组件,静态/动态对总包体积影响不大,但动态能让首屏更快。**


三、加载行为差异

静态导入

复制代码
用户访问 PageA
  ↓
浏览器下载 PageA.\[hash].js(含所有静态导入的依赖)
  ↓
Vue 直接渲染,组件立即可用
  ↓
跳转 PageB
  ↓
浏览器下载 PageB.\[hash].js

动态导入

复制代码
用户访问 PageA
  ↓
浏览器下载 PageA.\[hash].js(不含动态导入的组件)
  ↓
Vue 渲染到使用了动态组件的部分
  ↓
发起额外 HTTP 请求下载 BigTable.\[hash].js
  ↓
下载完成,组件显示
  ↓
跳转 PageB
  ↓
浏览器下载 PageB.\[hash].js
  ↓
PageB 也用到 BigTable ------ 从缓存取,无需再次下载

四、对比总表

维度 静态导入 动态导入
语法 import X from './X' () => import('./X')
执行时机 编译时(模块初始化前) 运行时(渲染时才请求)
写在哪儿 文件顶层 任意位置(函数/条件/循环)
返回类型 模块引用(同步) Promise(异步)
同一组件 N 处引用 打包 N 份 打包 1 份
运行时加载 随父 chunk 一起下载 多一次 HTTP 请求
首屏可用性 立即可用 需要等待网络
路由懒加载 不支持 支持
已全局静态注册的组件 --- 不产生独立 chunk,零额外体积

五、选型建议

使用场景 推荐方式 理由
路由页面组件 动态导入 路由懒加载,首屏只加载当前页面
低频弹窗(点击才出现) 动态导入 用户不点就不加载
全局注册的通用组件(global.js 已有) 两者等效 已在主 chunk,无差别
高频使用、切换频繁的组件 静态导入 省去每次切换时的网络等待
同一个组件被 2+ 页面引用 动态导入 避免重复打包
只有 1 个页面用的小组件 两者等效 打包体积无差异
第三方库(element-ui、vxe-table) 静态导入 按需引入或全量引入,不用动态分包

六、实际项目中的模式

js 复制代码
// ✅ 推荐:路由懒加载用动态导入
{
  path: '/salary-manage',
  component: () => import('@/views/salary-manage/index.vue')
}

// ✅ 推荐:低频使用组件用动态导入
components: {
  UploadAttach: () => import('@/components/UploadAttach/index.vue')
}

// ⚠️ 多余但无害:组件已在全局静态导入,再写动态
components: {
  dialogAddEdit: () => import('@/components/form/addEdit') // ← 已在 global.js 静态导入
}

// ❌ 不推荐:高频切换的组件用动态导入
// 如果用户频繁在 A/B tab 间切换,每次切换都等网络
components: {
  TabAComp: () => import('./TabAComp.vue'),
  TabBComp: () => import('./TabBComp.vue')
}
// → 改为静态导入
import TabAComp from './TabAComp.vue'
import TabBComp from './TabBComp.vue'

七、怎么验证打包结果

bash 复制代码
# 构建
npm run build

# 查看产物
# 打开 dist/assets/ 目录
# 静态导入的组件代码会混在页面 chunk 里
# 动态导入的组件会生成独立的 .js 文件

# 也可以看构建日志末尾的 chunk 列表:
# dist/assets/index.\[hash].js        ← 主入口
# dist/assets/BigTable.\[hash].js     ← 动态导入的独立 chunk
# dist/assets/PageA.\[hash].js        ← 页面 chunk

八、一句话口诀

复制代码
多处引用用动态,避免重复打包;
首屏路径用静态,省下一次请求;
全局已经注册过,动静态一样;
低频弹窗懒加载,分包体积最优。
相关推荐
半句唐诗17 小时前
我是如何通过 Access Token 成功发布第一个 npm 包的
前端·npm·node.js
程序员黑豆17 小时前
鸿蒙应用开发:@Provider 与 @Consumer 跨组件双向同步详解
前端·harmonyos
paopaokaka_luck17 小时前
基于springboot3+vue3的智能文库平台(AI智能搜索、AI智能汇总、实时在线状态展示、多格式文档预览与富文本编辑、Echarts图形化分析)
前端·网络·spring boot·网络协议·echarts
牧艺17 小时前
cos-design PhotoAlbum:用 CSS 3D 做一个「能翻页」的实体相册
前端·css·交互设计
洪贺17 小时前
从原始采样到可缩放心电图:用 h5ECG 绘制自己的 ECG
前端
冰心孤城17 小时前
Excel: xls与xlsx格式转换排坑指南
java·前端·excel
程序员黑豆18 小时前
鸿蒙应用开发之跨组件传参:@Provide 与 @Consume 跨层级数据同步详解
前端·harmonyos
0_Error_18 小时前
猫国建设者 修改资源 自动采集
javascript
贩卖黄昏的熊18 小时前
NestJS简明教程——异常处理和日志
javascript·node.js·nest.js
C++、Java和Python的菜鸟19 小时前
第9章 后端Web进阶(AOP)
java·开发语言·前端