在前端开发中,我们常用import dayjs from "dayjs"
(默认导入)或import { useState } from 'react'
(具名导入)引入依赖,但像ECharts这类库却必须用import * as echarts from 'echarts'
才能正常使用。本文以ECharts为例,结合ES Module(ESM)与CommonJS(CJS)规范差异,拆解这一现象的底层原因。
一、先明确:ES Module的3种核心导入方式
在ESM规范中,导入方式对应不同的导出逻辑,这是理解“为何需import *
”的基础:
导入方式 | 语法示例 | 适用场景 | 依赖导出要求 |
---|---|---|---|
默认导入 | import xxx from 'module' |
模块有且仅有一个核心导出(如dayjs) | 依赖必须通过export default xxx 导出 |
具名导入 | import { a, b } from 'module' |
仅需导入模块中的部分指定内容(如React Hooks) | 依赖需通过export const a 等具名导出 |
命名空间导入 | import * as mod from 'module' |
需导入模块的所有导出内容(如ECharts) | 依赖无export default ,仅具名导出或整体导出 |
二、核心原因:以ECharts为例,看依赖的导出逻辑
ECharts必须用import *
,本质是由其导出方式决定的——它没有提供ESM的export default
,而是采用“兼容多模块规范”的导出策略。

程序员导航
优网导航旗下整合全网优质开发资源,一站式IT编程学习与工具大全网站
1. 先看ECharts的入口文件导出代码
ECharts入口文件(如index.js
)的关键导出代码如下,这三行对应三种模块兼容逻辑:
// 1. 从内部模块导入所有内容,挂载到echarts对象上(无默认导出,均为具名/整体导出)
import * as echarts from './types/dist/echarts';
// 2. 兼容UMD规范:浏览器通过<script>引入时,挂载到window.echarts全局变量
export as namespace echarts;
// 3. 兼容CommonJS规范:相当于module.exports = echarts,适配Node.js或老打包工具
export = echarts;
从代码可见:ECharts没有写export default echarts
,因此无法用import echarts from 'echarts'
(默认导入)——默认导入要求依赖必须有export default
,否则会报“undefined”错误;而import * as echarts
能将模块所有导出(这里是echarts
对象)整合到命名空间下,正好匹配其导出逻辑。
2. 为什么ECharts不做export default
?
若ECharts添加export default echarts
,理论上可支持默认导入,但官方未这么做,核心原因有三点:
- 多规范兼容性需求:ECharts需同时兼容ESM(现代前端)、CJS(Node.js/老工具)、UMD(浏览器
<script>
引入)。export = echarts
是TypeScript环境下兼容多规范的最优方案,而export default
会增加CJS/UMD的适配复杂度(可能导致部分老环境报错)。 - 历史包袱:ECharts早期基于UMD规范开发(早于ESM普及),当时的导出逻辑是“挂载到全局变量或
module.exports
”;后续迁移到TypeScript时,为避免破坏存量项目,延续了export = echarts
的兼容写法,未强行改为export default
。 - API设计语义一致性:ECharts的定位是“命名空间式API”,用户习惯通过
echarts.init()
、echarts.setOption()
等“命名空间.方法”的形式调用。若用export default
,虽语法上可行,但会打破这种长期形成的使用习惯,增加开发者认知成本。
三、import *
的其他常见应用场景
除了ECharts,以下场景也常需用import *
导入:

AI 工具导航
优网导航旗下AI工具导航,精选全球千款优质 AI 工具集
1. 工具类库(需使用大量工具函数)
当工具库导出众多函数,且需频繁使用多个函数时,import *
可避免逐个具名导入,写法更简洁。例如lodash-es
:
// 无需逐个导入uniq、map、filter等
import * as _ from 'lodash-es';
const arr = [1,2,2,3];
console.log(_.uniq(arr)); // 去重:[1,2,3]
console.log(_.map(arr, item => item*2)); // 映射:[2,4,4,6]
2. 配置性常量/枚举集合
若模块仅导出大量常量(如系统配置、枚举值),import *
可将所有常量整合到一个对象,方便管理。例如Node.js的path
模块(ESM版本):
import * as path from 'path';
// 直接通过path对象访问所有常量/方法
console.log(path.sep); // 系统路径分隔符(Windows为\,Linux/Mac为/)
console.log(path.resolve('./src')); // 解析绝对路径
四、实用技巧:如何判断依赖该用哪种导入方式?
遇到不熟悉的依赖时,可通过以下3种方式快速确定导入方式:
- 优先看官方文档:几乎所有库的“快速上手”都会明确给出导入示例(如ECharts文档直接标注
import * as echarts from 'echarts'
),这是最直接的方式。 - 查看依赖的源码/类型文件:若没有文档,可在
node_modules
中找到依赖目录,查看入口文件(如index.js
)或TypeScript类型文件(index.d.ts
),通过导出代码判断(有无export default
、导出方式是export =
还是具名导出)。 - 借助AI编译器提示:若依赖无类型文件(无
index.d.ts
),可通过Trae、Cursor等AI编译器的智能提示获取导入建议,减少试错成本。
总结
依赖是否需要import *
,本质由模块的导出方式决定:当依赖无export default
(仅具名导出或采用export =
兼容写法),且需导入其所有内容时,必须用命名空间导入(import *
)。这一现象背后,不仅是ESM规范的语法要求,更体现了库作者对“多模块规范兼容”“历史项目适配”“API语义一致性”的考量。理解这一点,能帮我们更灵活地应对不同依赖的导入场景,避免因语法问题浪费调试时间。