为什么有些依赖必须 import *引入使用?
Hi, 我是石小石~
在日常前端开发中,我们常常通过 import 来引入第三方库或模块。像这样:
import dayjs from "dayjs"import { useState, useEffect } from 'react';
但你可能遇到过这样一种情况:有些依赖如果直接写成
import echarts from 'echarts';会报错,或者运行时报 echarts is undefined,而必须写成:
import * as echarts from 'echarts';这种现象可能会让你感到疑惑:「为什么 ECharts 必须使用」import *「的方式?难道普通的默认导入不行吗?」
本文将以 「ECharts」 为例,深入探讨这一问题的根源,并顺带梳理 「ES Module(ESM)与 CommonJS(CJS)模块规范的差异」,帮助你从底层理解 import * 背后的原因。
import 的几种常见用法
在 ES Module 规范中,常见的几种导入方式包括:
默认导入
import xxx from 'module';要求模块导出中必须有 export default xxx;。
具名导入
import { a, b } from 'module';要求模块内部有 export const a、export const b 等具名导出。
命名空间导入
import * as mod from 'module';将模块的所有导出整合到一个对象 模块 上。
通过 ECharts 的导出方式深入 import *
ECharts 的导出方式
熟悉完 ES6 的import的命名空间导入用法,我想echarts为什么需要通过import *答案已经很明确了。我们来看看 echarts 的入口文件:
这三句话分别代表了三种不同的模块兼容策略:
import * as echarts from ...
表示 ECharts 的所有导出都是 「具名导出」,并没有export default。export as namespace echarts;
这是为了兼容 「UMD 模块」,即在浏览器通过<script>直接引入时,可以挂载到全局变量window.echarts上。这样我们在代码里就能直接写:
echarts.init(document.getElementById('main'));export = echarts;
这是 「CommonJS 风格的导出」,相当于module.exports = echarts,用于兼容 Node.js 或老的打包工具。在这种情况下,TypeScript 需要你用import * as echarts来导入,而不是import echarts from 'echarts'。
为什么 ECharts 没有用 export default
看到这里,你可能还是有些困惑,如果导出入口改成
import * as echarts from './types/dist/echarts';// Export for UMD module.export as namespace echarts;export default echarts;
我们代码里不是就可以使用下面的导入方式了吗?
import echarts from 'echarts';原因主要有三点:
兼容性考虑
ECharts 是一个全球用户量非常大的可视化库,既要兼容 「ESM」,又要兼容 「CommonJS」,还要兼容 「UMD」(通过 <script> 引入时挂到 window.echarts 上)。
如果用 export default,对 CJS/UMD 的支持会比较麻烦,而 export = 在 TypeScript 下能天然兼容多种模块系统。
历史包袱
ECharts 最早是基于 UMD 格式发布的(早于 ES Module 普及)。
当时的入口写法更偏向:
if (typeof module === 'object' && module.exports) {module.exports = echarts;} else {window.echarts = echarts;}
后来迁移到 TypeScript 时,延续了 export = echarts 这种兼容写法,而不是强行改为 export default。
API 设计思路
ECharts 官方的定位是 “命名空间式 API”,也就是说,用户习惯通过 echarts.xxx() 方式调用。
为了保持这种语义一致性,团队没有提供 default export,而是把所有 API 都挂在一个 echarts 命名空间对象下。
import* 的其他应用场景
除了 ECharts,还有一些库或工具函数常常需要使用 import *:
工具类库
有些工具库导出了非常多的函数,如果不想逐个引入,可以整合为一个命名空间对象。「例如:lodash-es」
import * as _ from 'lodash-es';const arr = [1, 2, 2, 3, 4];console.log(_.uniq(arr)); // [1, 2, 3, 4]
配置性常量集合
当一个模块只导出大量的常量或枚举值时,import * 可以避免逐个列出,写法更整洁。
「例如:Node.js 内置」path「模块(ESM 版本)」
import * as path from 'path';console.log(path.sep); // 当前系统路径分隔符(Windows: '', Linux/Mac: '/')console.log(path.resolve('./src'));
如何判断是否需要 import *
如果你不确定一个库该怎么导入,可以采用以下几种方式:
看文档示例 
查看源码或 d.ts 文件
如果不想找文档,可以直接打开项目的依赖
找到对应的包,导出方式在 index.d.ts 文件中可以看到
Trae/Cursor 智能提示
并不是所有第三方库都内置 Ts 支持,有 index.d.ts 文件,这个时候你可以借助 AI 编译器(如 Trae 或 Cursor)的智能提示。
以 「Trae」 的注释生成代码为例: