import / export 语法与静态分析
本节目标
- 掌握 ESM 的
import/export全部写法 - 理解「静态」带来的好处(编译期可知依赖)
- 区分「命名导出」与「默认导出」
// 运行环境:Node.js 16+(保存为 .mjs 或开 type:module)
// math.mjs —— 命名导出(推荐,可多个)
export const add = (a, b) => a + b
export function mul(a, b) { return a * b }
const secret = 1 // 没 export,外部拿不到(私有)
// 默认导出(一个模块只能一个)
export default function main() {}
// 导入方
import main, { add, mul } from './math.mjs' // 默认 + 命名解构
import * as Math from './math.mjs' // 整体导入为命名空间
console.log(add(1, 2), mul(2, 3), main)
// ⚠️ 静态:import 必须顶层,不能写进 if
// if (x) import './a.mjs' // ❌ 语法错误为什么「静态」好:因为依赖在编译期就全确定了,打包器(Vite/Webpack)能提前画好「模块图」,做 Tree Shaking(删掉没用到的导出),IDE 能精准跳转到定义。这是 ESM 碾压 CJS 的工程优势。
名词解释
命名导出(Named Export):export const/add/function 这种「带名字的导出」,一个模块可以有多个。导入时用 { name } 解构对应名字。它比默认导出更利于 Tree Shaking(因为名字明确,没用到的能删)。
静态分析(Static Analysis):在不「运行代码」的前提下,仅靠读源码就能确定信息(如「这个模块 import 了谁」)。ESM 的 import 是静态语法,所以工具能在编译期分析依赖。这正是 Tree Shaking、类型检查、重构安全的前提。CJS 的 require 是运行时调用,做不到这点。
课后练习
练习 1:import x from './m' 和 import { x } from './m' 区别?
答案:前者取「默认导出」
export default的值(名字 x 随意);后者取「命名导出」export const x的值(名字必须匹配)。混用是常见错误来源。
练习 2:为什么 ESM 能做 Tree Shaking 而 CJS 很难?
答案:ESM 导出是静态、命名的,打包器编译期就知道「哪些命名导出没被任何人 import」,整段删掉;CJS 的
module.exports是运行时对象,没法静态判断某个属性会不会被用,只能保守保留。静态是 Tree Shaking 的前提。
总结
ESM 的语法我建议「默认导出能免则免,命名导出为主」。我的观点:命名导出(export const fn)让每个导出都有名字,配合 Tree Shaking 和 IDE 跳转体验最好;export default 虽然省事,但一个模块多个 default 装进一个匿名值,重构和静态分析都吃亏。静态性是 ESM 的灵魂——它把「依赖关系」变成编译期可知的事实,这才有了现代前端工程的速度(Tree Shaking 让打包体积小一大截)。一句话:写 ESM 时想着「工具要靠静态分析帮你」,你的写法就会更友好。