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

八、一句话口诀

复制代码
多处引用用动态,避免重复打包;
首屏路径用静态,省下一次请求;
全局已经注册过,动静态一样;
低频弹窗懒加载,分包体积最优。
相关推荐
IT_陈寒11 分钟前
为什么你应该学习React?
前端·人工智能·后端
ZoeLandia12 分钟前
React04:事件处理与受控表单
开发语言·前端·react.js·typescript
亿元程序员16 分钟前
用AI做了一款微信小游戏,却发现上传不了
前端
Ai-_Man21 分钟前
我需要批量导出Poe左边栏所有对话,而不是一条具体的对话内容,你们AI导出鸭可以吗?
开发语言·前端·javascript·人工智能·小程序
java1234_小锋22 分钟前
【技术专题】HTML5 网页技术 - Web Storage 本地存储 & 文件 API & 拖放 API
前端·html·html5
恋猫de小郭23 分钟前
Flutter 官方表示:AI 时代代码越来越容易写,为什么还要一套代码库?
android·前端·flutter
软件派30 分钟前
Chrome 插件开发实战指南
前端·chrome
ly768930 分钟前
性能剖析与压测方法论:JMH、async-profiler 与分层瓶颈定位的可复现实验
服务器·前端·数据库·压测·jmh·性能剖析·async-profiler
ZoeLandia43 分钟前
React05:useEffect 与副作用
开发语言·前端·react.js·typescript
狮子座的男孩1 小时前
html+css基础:24、2D变换(位移/缩放/旋转/扭曲(了解)/多重变换/变换原点)
前端·css·经验分享·html·2d位移·原点变换·缩放/旋转/扭曲