ES6 模块是 JavaScript 官方的模块化方案,通过 import 和 export 实现代码的模块化组织。
export 导出#
命名导出#
export const PI = 3.14159
export function add(a, b) { return a + b}
export function multiply(a, b) { return a * b}
export class Calculator { // ...}声明后导出#
const name = '工具库'
function format(str) { return str.trim()}
function parse(str) { return JSON.parse(str)}
// 统一导出export { name, format, parse }导出时重命名#
function internalHelper() { // 内部实现}
function privateUtil() { // 内部使用}
export { internalHelper as helper, privateUtil as util }默认导出#
export default class Person { constructor(name) { this.name = name; }}
// 或者class Person { constructor(name) { this.name = name; }}
export default Person;默认导出与命名导出混用#
export default function request(url) { return fetch(url)}
export const GET = 'GET'export const POST = 'POST'
export function createHeaders(token) { return { Authorization: `Bearer ${token}` }}默认导出的本质#
// 默认导出实际上是导出一个名为 default 的变量export default function() {}// 等价于function _default() {}export { _default as default };
// 所以可以这样导入import { default as myFunc } from './module.js';import 导入#
导入命名导出#
// 导入指定成员import { add, multiply } from './math.js'
// 导入时重命名import { add as sum, multiply as mul } from './math.js'
// 导入所有命名导出import * as math from './math.js'math.add(1, 2)math.multiply(3, 4)导入默认导出#
// 默认导出可以用任意名称导入import Person from './person.js'import MyPerson from './person.js' // 名称随意
// 等价于import { default as Person } from './person.js'同时导入默认和命名导出#
import request, { GET, POST, createHeaders } from './api.js'
// 或者import request, * as api from './api.js'api.GET // 'GET'仅执行模块#
// 不导入任何内容,只执行模块代码import './polyfill.js'import './init.js'模块路径#
相对路径#
import { helper } from './helpers.js'import { utils } from '../shared/utils.js'import config from './config/index.js'包名导入#
import React from 'react'import { useState, useEffect } from 'react'import lodash from 'lodash'子路径导入#
import { Button } from '@mui/material'import { format } from 'date-fns/format'import cloneDeep from 'lodash/cloneDeep'export from 语法#
转发导出#
// index.js - 聚合模块export { add, multiply } from './math.js'export { format, parse } from './utils.js'export { default as Person } from './person.js'转发所有导出#
// 转发 math.js 的所有命名导出export * from './math.js'
// 注意:不包括默认导出// 默认导出需要单独处理export { default } from './math.js'// 或重命名export { default as math } from './math.js'重命名转发#
export { add as sum } from './math.js'export { default as MathUtils } from './math.js'模块聚合实践#
export { default as Button } from './Button.js'export { default as Input } from './Input.js'export { default as Modal } from './Modal.js'export { default as Table } from './Table.js'
// 使用时import { Button, Input, Modal } from './components'动态导入#
import() 函数#
// 动态导入返回 Promiseconst module = await import('./module.js')
// 或使用 thenimport('./module.js').then((module) => { module.doSomething()})条件导入#
async function loadFeature(featureName) { if (featureName === 'chart') { const { Chart } = await import('./features/chart.js') return new Chart() } if (featureName === 'editor') { const { Editor } = await import('./features/editor.js') return new Editor() }}懒加载#
// 按钮点击时才加载button.addEventListener('click', async () => { const { openDialog } = await import('./dialog.js') openDialog()})动态路径#
async function loadLocale(lang) { const locale = await import(`./locales/${lang}.js`) return locale.messages}
// 使用const messages = await loadLocale('zh-CN')模块的静态结构#
编译时确定#
// ES6 模块是静态的,以下写法不允许:
// ❌ 不能条件导入if (condition) { import { foo } from './foo.js'; // SyntaxError}
// ❌ 不能动态路径(静态 import)import { foo } from './' + name + '.js'; // SyntaxError
// ❌ 不能在块作用域中{ import { foo } from './foo.js'; // SyntaxError}静态分析的好处#
// 1. Tree Shaking - 未使用的导出可以被移除import { used } from './utils.js'// unused 不会被打包
// 2. 提前发现错误import { typo } from './module.js'// 如果 typo 不存在,编译时就能发现
// 3. 更好的 IDE 支持import { func } from './module.js'// IDE 可以提供自动补全和跳转模块的特性#
严格模式#
// ES6 模块自动采用严格模式// 不需要 'use strict'
export function example() { // this 是 undefined,不是 window console.log(this)}顶层 this#
// 模块顶层的 this 是 undefinedconsole.log(this) // undefined
// 不是 window 或 global导出的是引用#
export let count = 0
export function increment() { count++}
// main.jsimport { count, increment } from './counter.js'
console.log(count) // 0increment()console.log(count) // 1(是引用,不是值的复制)模块只执行一次#
console.log('模块初始化')export const value = Math.random()
// a.jsimport { value } from './init.js'// 打印 '模块初始化'console.log(value) // 0.123...
// b.jsimport { value } from './init.js'// 不会再打印,模块已执行过console.log(value) // 0.123...(同一个值)import.meta#
获取模块元信息#
// 获取当前模块的 URLconsole.log(import.meta.url)// file:///path/to/module.js// 或 https://example.com/module.js
// 获取当前目录const dirname = new URL('.', import.meta.url).pathname实际应用#
// 加载相对于当前模块的资源const imageUrl = new URL('./image.png', import.meta.url)
// 读取相邻的 JSON 文件(Node.js)import { readFile } from 'fs/promises'const configPath = new URL('./config.json', import.meta.url)const config = JSON.parse(await readFile(configPath, 'utf8'))循环依赖#
循环依赖的处理#
import { b } from './b.js'export const a = 'a'console.log('a.js:', b)
// b.jsimport { a } from './a.js'export const b = 'b'console.log('b.js:', a)
// main.jsimport './a.js'// 输出:// b.js: undefined(a.js 还没执行完)// a.js: b解决循环依赖#
// 方案1:重构代码,消除循环依赖
// 方案2:使用函数延迟访问import { getB } from './b.js'export const a = 'a'export function getA() { return a}console.log('a.js:', getB())
// b.jsimport { getA } from './a.js'export const b = 'b'export function getB() { return b}console.log('b.js:', getA())最佳实践#
导出风格一致#
// ✅ 推荐:一个文件一个主要导出export default function Button() {}
// ✅ 推荐:工具文件使用命名导出// utils.jsexport function formatDate() {}export function formatNumber() {}
// ❌ 避免:既有默认导出又有大量命名导出export default class API {}export const GET = 'GET';export const POST = 'POST';// ... 更多导出使用 index.js 聚合#
export { default as Button } from './Button.js'export { default as Input } from './Input.js'
// 使用import { Button, Input } from './components'避免命名空间导入滥用#
// ❌ 避免:导入所有可能影响 Tree Shakingimport * as utils from './utils.js'utils.format()
// ✅ 推荐:只导入需要的import { format } from './utils.js'format()小结#
| 语法 | 说明 |
|---|---|
| export | 导出模块成员 |
| export default | 默认导出 |
| import | 导入命名导出 |
| import x | 导入默认导出 |
| import * as | 命名空间导入 |
| import() | 动态导入 |
| export from | 转发导出 |
ES6 模块是 JavaScript 模块化的标准方案,掌握它对于现代前端开发至关重要。