前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库ESM named export vs default export
JavaScript模块与语言机制

具名导出和默认导出有什么区别?什么时候该用哪种?

具名导出用 export 关键字标记,导入时必须用大括号写对名字。默认导出用 export default,每个模块只能有一个,导入时可以起任意名字。两者可以在同一个模块里混用。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#模块与语言机制
先看核心答案读代码示例
理解线索

两种导出方式的对比

  1. 具名导出export const x = 1,导入时 { x },名字必须对应
  2. 默认导出export default expr,导入时 import anyName,一个模块最多一个
  3. 混合使用同一模块可以同时有具名和默认导出,导入时 import def, { a } 一起写

重导出时要注意:export { default } from './a.js' 可以把 a 的默认导出当具名 default 再导出,语法上容易搞混。

核心回答

先记住这个答案

具名导出(named export)通过 export 把变量、函数或类导出,一个模块可以有任意多个。导入方必须用大括号并且写对名字,比如 import { formatDate } from './utils'。默认导出(default export)用 export default,每个模块最多一个。导入方可以起任意名字,比如 import Whatever from './Button'。两者可以在同一个模块里共存。

  • 具名导出可以有多个,导入时名字必须对上
  • 默认导出每个模块最多一个,导入时可以随意命名
  • 具名导出对 tree-shaking 和 IDE 自动补全更友好
  • 默认导出适合模块只有一个主要导出的情况

语法对比:两种导出长什么样

先看代码,把两种写法放在一起就能看清区别。

具名导出和默认导出的写法JavaScript
// utils.js —— 具名导出
export const PI = 3.14159
export function formatDate(date) {
  return date.toISOString().slice(0, 10)
}

// Button.js —— 默认导出
export default function Button(props) {
  return `<button>${props.label}</button>`
}

// 导入方式
// import { PI, formatDate } from './utils'   具名:大括号 + 对应名字
// import MyButton from './Button'            默认:任意名字,不用大括号
// import Button, { PI } from './combined'    混合:默认在前,具名在后

具名导出的名字是固定的。写 import { PI } from './utils' 的时候,PI 必须和导出端的名字一模一样。想换名字要显式用 as,比如 import { PI as pi } from './utils'。默认导出没有这个限制,导入端想叫什么就叫什么。

实际项目里怎么选

工具库、常量文件这类一个模块导出很多东西的场景,适合具名导出。React 组件、页面入口这类一个文件就是一个主要"东西"的场景,适合默认导出。

具名导出对 IDE 更友好。你输入 import { 之后,IDE 能根据目标模块的导出列表给你补全可用的名字。默认导出做不到这一点——IDE 不知道你想叫什么名字,只能猜一个文件名。

另一个区别在 tree-shaking 上。具名导出让打包工具清楚地知道哪些导出被用到了,没用到的可以安全删掉。默认导出本身不影响 tree-shaking,但如果默认导出的是一个大对象(比如 export default { a, b, c }),打包工具没办法分析对象里哪些属性被用了,只能全部保留。

大型团队有时会规定"只用具名导出",避免不同人给同一个默认导出起不同的名字导致 import Button 和 import Btn 指向同一个东西但在代码里看不出来的问题。

export default 背后的特殊之处

export default 在语法层面其实是把值绑定到一个叫 default 的具名导出上。所以 import Button from './Button' 等价于 import { default as Button } from './Button'。理解这一点能帮你看懂一些重导出的写法。

export default 后面如果跟的是表达式(比如 export default 42 或 export default someVar),它会立刻求值,把当前的值绑定到 default 导出。后续即使 someVar 变了,默认导出不会跟着变。但如果用 export { someVar as default } 这种写法,就是实时绑定的——源模块改了 someVar,导入方也能读到新值。

回答前,多想一步

容易答错的地方

以为默认导出和具名导出不能共存
一个模块完全可以同时有默认导出和多个具名导出。React 库就是这样做的——默认导出 React 对象,同时具名导出 useState、useEffect 这些 Hook。导入时写 import React, { useState } from 'react' 就行。
以为具名导出导入时可以随便改名
具名导出导入时名字必须对上,想改名只能用 as。写 import { formatDate as fmt } from './utils' 是对的,直接写 import { fmt } from './utils' 会报模块里不存在 fmt 这个导出。
试着用自己的话回答

面试官还会怎么问?

把多个具名导出收进一个对象再默认导出,好不好?

不好。export default { formatDate, parseDate } 这样写会让 tree-shaking 失效,因为打包工具只能看到一个对象被导出了,没办法判断对象里的 parseDate 到底有没有被外部用到。直接用具名导出,让每个函数都是独立的导出项,tree-shaking 才能工作。

重导出(re-export)怎么处理默认导出?

export { default } from './Button' 把 Button 的默认导出当作具名的 default 再导出。export { default as Button } from './Button' 把它改名成 Button 再导出。用这些写法可以在 index.js 里把多个模块的默认导出统一收拢成具名导出。

TypeScript 里的 export = 和 import = require() 是怎么回事?

这是 TypeScript 兼容 CommonJS 模块的写法。export = 等价于 module.exports =,import x = require('./a') 等价于 CJS 的 require。在纯 ESM 项目里不需要这些写法,用标准的 export default 和 import 就够了。

从一道题,走向一组知识

把知识连起来

模块与语言机制

用 const 和 let 导出的变量,在实时绑定上有什么区别?

具名导出用 const 和 let 声明时,实时绑定的行为有差别,该题专门分析这个区别。

模块与语言机制

import * as ns 和具名导入在 tree-shaking 上有什么区别?

import * as ns 会把具名导出和默认导出都收进命名空间对象,了解两种导出有助于理解 ns.default 的由来。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • export

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 语法对比:两种导出长什么样
  3. 实际项目里怎么选
  4. export default 背后的特殊之处
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑