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

八、一句话口诀

复制代码
多处引用用动态,避免重复打包;
首屏路径用静态,省下一次请求;
全局已经注册过,动静态一样;
低频弹窗懒加载,分包体积最优。
相关推荐
To_OC5 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC5 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic5 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy5 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志6 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长7 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜8 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司8 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能