先记住这个答案
具名导出将模块内变量标记为可外部引用,导入需在花括号内列举其名称(可用 as 改名),例如 import { format } from './m.js';默认导出导出的值是模块直接视为“主值”,导入写成 import fmt from './m.js'。一个模块能拥有多个具名导出但不能拥有两个默认导出。具名导出保留了原始标识符,工具链更易检查;默认导出把命名自由交给接收方,可能造成检索不稳定。用在库或长期维护代码时,优先具名;仅在模块主入口和单值场景使用默认。
- 默认导出每模块只能一个,具名可多个
- 默认导出的导入名由接收方决定,具名由来源确定
- 公开主值用默认,成组 API 用具名导出
语法与导入标识符的差异
具名导出的本质是在模块导出表里登记一个标识符。export const parse = s => s.trim() 之后,导入语句必须写 parse 或用 as 改成别名,但不能省略花括号。默认导出则生成名为 default 的特殊入口,接收方写 import p from './m.js' 时,p 就是模块自行决定的一个本地名,源模块内不要求存在同名变量。
两种导出共享同一个导出命名空间,默认导出登记的条目名就是 default。因此冲突只发生在导出名重复时:例如写两个 export default,或同时写 export { x as default } 和 export default y,都会在解析阶段抛出 SyntaxError。而 export default function parse(){} 之后再写 export { parse } 是合法的——前者以 default 为名、后者以 parse 为名,只是让同一个函数从两个名字被引入。默认导出每模块唯一这一约束源自 default 只是普通导出名的规范处理,并非独立的第二个通道。
校验工具库的导出设计
某模块要暴露 isEmail 和 normalizeEmail 两个独立纯函数。如果默认导出整个对象,调用方必须写 import email from './validator' 再加 .isEmail,编辑器无法直接发现可用的函数清单,打包器也有可能因保留对象属性而保留多余代码。改为具名导出后,import { isEmail } from './validator' 精确得到函数且能触发重命名联动。
这个场景里默认导出没有起简化作用,反而增加一层壳。真正需要默认导出的条件是:模块只有一个主要值,且该值不依赖外部继续解构,比如单个 createStore 函数。若预感到会加入 createStoreWithMiddleware,设计时就应用具名或用命名空间对象配合具名导出。
默认导出的可见成本与限制
默认导出的最大限制不是数量,而是丢失规范性的名称。同一个 parse 函数可能被不同文件命名为 parseCsv、load,导致代码库出现同一功能多种叫法,grep 时无法用统一符号定位。因此不建议在公开类库或多人维护项目中满屏默认导出。
一旦模块已经提供默认导出,后续重导出需要专门语法:export { default as Foo } from './a.js' 有效,export default from './a.js' 是语法错误。如果模块包含默认加多个具名,会迫使主要依赖方使用两套导入风格,维护成本上升。没有稳定主值前,保持全部具名可让未来迁移成本最低。
容易答错的地方
- 默认导出跟 const 声明不搭
- 不少人写
export default const x = 1,这是SyntaxError,因为default后面可跟表达式或函数/类声明,不能跟带声明关键字的语句。正确为const x = 1; export default x;或export default 1。 - 默认和具名可共存,但默认只能有一个
- ESM 允许
export default main与export { helper }同时出现。真正的坑是重复绑定:export { x as default }和export default y不能同用,因为二者都会登记default,触发重复导出错误。
面试官还会怎么问?
什么时候最好不要用默认导出?
当模块的导出会构成工具集、会被远端静态检查或需要按需打包时不要默认导出。默认导出隐藏函数真实名字,不利于重命名和重构,因此标准做法是把核心 API 全部具名导出。
默认导出真的会让 tree-shaking 变差吗?
与打包器有关。默认导出一个对象会使整个对象作为一段被保留,属性级摇树较难;若默认导出的只是纯函数且无副作用,多数打包器可处理。想享受稳定的体积优化,仍优先使用具名导出。
代码规范里怎样统一两种导出?
最小规则是:一个模块只允许一种导出方式,除非默认导出是组件或外部约定的主入口。选择具名还是默认要看模块未来是提供一组能力还是表示一个值,在文件头注释里写明依据。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。