Search K
Appearance
Appearance
📊 SEO元描述:2024年最新ES6模块化教程,详解import/export语法、默认导出vs命名导出、ES2020动态导入、模块化最佳实践。包含完整实战案例,适合JavaScript开发者掌握现代模块化开发。
核心关键词:ES6模块化2024、import/export语法、默认导出命名导出、动态导入、JavaScript模块化、ES2020模块化
长尾关键词:ES6模块化怎么用、import export详解、JavaScript模块化开发、动态导入import()、模块化最佳实践
通过本节ES6模块化教程,你将系统性掌握:
为什么需要ES6模块化?这是现代JavaScript开发工程化的核心问题。ES6模块化提供了原生的模块系统,解决了代码组织、依赖管理、命名空间等问题,也是现代JavaScript开发的基础设施。
💡 核心原则:模块化让代码组织更清晰,依赖关系更明确,是现代JavaScript开发的必备技能
ES6模块化通过import和export关键字实现模块的导入和导出。
// 🎉 import/export基本语法详解
// ===== 导出模块示例 =====
// 📁 math.js - 数学工具模块
console.log('=== 命名导出 ===');
// 1. 命名导出 - 单个导出
export const PI = 3.14159;
export const E = 2.71828;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// 2. 命名导出 - 批量导出
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
const power = (base, exponent) => Math.pow(base, exponent);
export { subtract, divide, power };
// 3. 导出时重命名
const square = x => x * x;
const cube = x => x * x * x;
export {
square as getSquare,
cube as getCube
};
// 📁 calculator.js - 计算器模块
console.log('=== 默认导出 ===');
// 4. 默认导出 - 类
export default class Calculator {
constructor() {
this.history = [];
}
calculate(operation, a, b) {
let result;
switch (operation) {
case 'add':
result = a + b;
break;
case 'subtract':
result = a - b;
break;
case 'multiply':
result = a * b;
break;
case 'divide':
result = a / b;
break;
default:
throw new Error('Unknown operation');
}
this.history.push({ operation, a, b, result });
return result;
}
getHistory() {
return this.history;
}
clear() {
this.history = [];
}
}
// 📁 utils.js - 工具模块
console.log('=== 混合导出 ===');
// 5. 混合导出(默认导出 + 命名导出)
export const VERSION = '1.0.0';
export const AUTHOR = 'Developer';
export function formatNumber(num, decimals = 2) {
return num.toFixed(decimals);
}
export function isEven(num) {
return num % 2 === 0;
}
// 默认导出一个配置对象
export default {
theme: 'light',
language: 'zh-CN',
timeout: 5000,
retries: 3
};
// ===== 导入模块示例 =====
// 📁 main.js - 主模块
console.log('=== 导入语法 ===');
// 1. 命名导入
import { PI, E, add, multiply } from './math.js';
console.log('常量:', PI, E);
console.log('加法:', add(5, 3));
console.log('乘法:', multiply(4, 6));
// 2. 导入时重命名
import { subtract as minus, divide as div } from './math.js';
console.log('减法:', minus(10, 4));
console.log('除法:', div(15, 3));
// 3. 导入重命名的导出
import { getSquare, getCube } from './math.js';
console.log('平方:', getSquare(5));
console.log('立方:', getCube(3));
// 4. 默认导入
import Calculator from './calculator.js';
const calc = new Calculator();
console.log('计算结果:', calc.calculate('add', 10, 20));
// 5. 混合导入
import config, { VERSION, AUTHOR, formatNumber } from './utils.js';
console.log('配置:', config);
console.log('版本:', VERSION);
console.log('作者:', AUTHOR);
console.log('格式化数字:', formatNumber(3.14159));
// 6. 全部导入
import * as MathUtils from './math.js';
console.log('全部导入:', MathUtils.PI);
console.log('全部导入函数:', MathUtils.add(1, 2));
// 7. 仅执行导入(副作用导入)
import './init.js'; // 仅执行模块,不导入任何内容
// ===== 高级导入导出模式 =====
// 📁 advanced.js - 高级模式
console.log('=== 高级导入导出 ===');
// 8. 条件导出
const isDevelopment = process.env.NODE_ENV === 'development';
export const config = {
apiUrl: isDevelopment ? 'http://localhost:3000' : 'https://api.example.com',
debug: isDevelopment,
logLevel: isDevelopment ? 'debug' : 'error'
};
// 9. 计算属性导出
const features = ['auth', 'payment', 'analytics'];
const modules = {};
features.forEach(feature => {
modules[feature] = {
enabled: true,
version: '1.0.0'
};
});
export { modules as featureModules };
// 10. 函数式导出
export const createLogger = (prefix) => {
return {
log: (message) => console.log(`[${prefix}] ${message}`),
error: (message) => console.error(`[${prefix}] ERROR: ${message}`),
warn: (message) => console.warn(`[${prefix}] WARN: ${message}`)
};
};
// 📁 re-export.js - 重新导出
console.log('=== 重新导出 ===');
// 11. 重新导出
export { PI, E } from './math.js';
export { default as Calculator } from './calculator.js';
// 12. 重新导出并重命名
export { add as sum, multiply as product } from './math.js';
// 13. 重新导出全部
export * from './utils.js';
// 14. 重新导出默认为命名
export { default as DefaultConfig } from './utils.js';
// ===== 实际应用示例 =====
// 📁 api/index.js - API模块组织
console.log('=== API模块组织 ===');
// 用户API
export const userAPI = {
async getUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
},
async createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
return response.json();
},
async updateUser(id, userData) {
const response = await fetch(`/api/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
return response.json();
}
};
// 产品API
export const productAPI = {
async getProducts(params = {}) {
const queryString = new URLSearchParams(params).toString();
const response = await fetch(`/api/products?${queryString}`);
return response.json();
},
async getProduct(id) {
const response = await fetch(`/api/products/${id}`);
return response.json();
}
};
// 默认导出API集合
export default {
user: userAPI,
product: productAPI
};
// 📁 components/index.js - 组件模块组织
console.log('=== 组件模块组织 ===');
// 基础组件
export class Button {
constructor(text, onClick) {
this.text = text;
this.onClick = onClick;
}
render() {
const button = document.createElement('button');
button.textContent = this.text;
button.addEventListener('click', this.onClick);
return button;
}
}
export class Input {
constructor(placeholder, type = 'text') {
this.placeholder = placeholder;
this.type = type;
}
render() {
const input = document.createElement('input');
input.type = this.type;
input.placeholder = this.placeholder;
return input;
}
}
// 复合组件
export class Form {
constructor() {
this.fields = [];
}
addField(field) {
this.fields.push(field);
}
render() {
const form = document.createElement('form');
this.fields.forEach(field => {
form.appendChild(field.render());
});
return form;
}
}
// 工具函数
export const componentUtils = {
addClass(element, className) {
element.classList.add(className);
},
removeClass(element, className) {
element.classList.remove(className);
},
toggleClass(element, className) {
element.classList.toggle(className);
}
};理解默认导出和命名导出的差异对于正确使用ES6模块很重要。
// 🎉 默认导出 vs 命名导出详解
console.log('=== 默认导出 vs 命名导出对比 ===');
// ===== 默认导出示例 =====
// 📁 logger-default.js
console.log('1. 默认导出示例:');
class Logger {
constructor(name) {
this.name = name;
}
log(message) {
console.log(`[${this.name}] ${message}`);
}
error(message) {
console.error(`[${this.name}] ERROR: ${message}`);
}
}
// 默认导出类
export default Logger;
// 📁 config-default.js
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
};
// 默认导出对象
export default config;
// 📁 utils-default.js
// 默认导出函数
export default function formatDate(date) {
return date.toISOString().split('T')[0];
}
// ===== 命名导出示例 =====
// 📁 logger-named.js
console.log('2. 命名导出示例:');
export class ConsoleLogger {
log(message) {
console.log(message);
}
}
export class FileLogger {
constructor(filename) {
this.filename = filename;
}
log(message) {
// 模拟写入文件
console.log(`写入${this.filename}: ${message}`);
}
}
export const LOG_LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3
};
// 📁 math-named.js
export const PI = 3.14159;
export const E = 2.71828;
export function sin(x) {
return Math.sin(x);
}
export function cos(x) {
return Math.cos(x);
}
export function tan(x) {
return Math.tan(x);
}
// ===== 使用对比 =====
// 📁 usage-comparison.js
console.log('3. 使用对比:');
// 默认导入 - 可以使用任意名称
import Logger from './logger-default.js';
import AppConfig from './config-default.js'; // 重命名
import formatDate from './utils-default.js';
const logger = new Logger('App');
logger.log('应用启动');
console.log('配置:', AppConfig.apiUrl);
console.log('格式化日期:', formatDate(new Date()));
// 命名导入 - 必须使用确切名称
import { ConsoleLogger, FileLogger, LOG_LEVELS } from './logger-named.js';
import { PI, sin, cos } from './math-named.js';
const consoleLogger = new ConsoleLogger();
const fileLogger = new FileLogger('app.log');
consoleLogger.log('控制台日志');
fileLogger.log('文件日志');
console.log('数学常量:', PI);
console.log('正弦值:', sin(PI / 2));
// ===== 适用场景对比 =====
console.log('4. 适用场景对比:');
// 默认导出适用场景
console.log('默认导出适用场景:');
console.log('- 模块只导出一个主要功能');
console.log('- 导出的是类、函数或配置对象');
console.log('- 希望导入时可以自由命名');
// 📁 database.js - 默认导出示例
class Database {
constructor(connectionString) {
this.connectionString = connectionString;
}
async connect() {
console.log('连接数据库...');
}
async query(sql) {
console.log(`执行查询: ${sql}`);
return [];
}
}
export default Database;
// 使用默认导出
import DB from './database.js'; // 可以命名为DB
import MyDatabase from './database.js'; // 也可以命名为MyDatabase
// 命名导出适用场景
console.log('\n命名导出适用场景:');
console.log('- 模块导出多个相关功能');
console.log('- 导出的是工具函数集合');
console.log('- 希望明确导入的内容');
// 📁 validators.js - 命名导出示例
export function validateEmail(email) {
return email.includes('@');
}
export function validatePhone(phone) {
return /^\d{10,11}$/.test(phone);
}
export function validateAge(age) {
return age >= 0 && age <= 150;
}
export const VALIDATION_MESSAGES = {
EMAIL: '邮箱格式不正确',
PHONE: '手机号格式不正确',
AGE: '年龄必须在0-150之间'
};
// 使用命名导出
import { validateEmail, validatePhone, VALIDATION_MESSAGES } from './validators.js';
// ===== 混合使用最佳实践 =====
console.log('5. 混合使用最佳实践:');
// 📁 http-client.js - 混合导出示例
class HttpClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async get(url) {
console.log(`GET ${this.baseURL}${url}`);
return { data: 'mock data' };
}
async post(url, data) {
console.log(`POST ${this.baseURL}${url}`, data);
return { success: true };
}
}
// 默认导出主要类
export default HttpClient;
// 命名导出相关常量和工具
export const HTTP_METHODS = {
GET: 'GET',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE'
};
export const HTTP_STATUS = {
OK: 200,
CREATED: 201,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
NOT_FOUND: 404,
SERVER_ERROR: 500
};
export function createAuthHeaders(token) {
return {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
}
// 使用混合导出
import HttpClient, { HTTP_STATUS, createAuthHeaders } from './http-client.js';
const client = new HttpClient('https://api.example.com');
const headers = createAuthHeaders('token123');
console.log('HTTP状态码:', HTTP_STATUS.OK);
// ===== 选择指南 =====
console.log('6. 选择指南:');
const exportGuide = {
defaultExport: {
when: [
'模块只有一个主要导出',
'导出的是类、主函数或配置',
'希望导入时命名灵活'
],
examples: [
'React组件',
'数据库连接类',
'配置对象',
'主要业务逻辑函数'
]
},
namedExport: {
when: [
'模块有多个相关导出',
'导出工具函数集合',
'导出常量和枚举',
'希望明确导入内容'
],
examples: [
'工具函数库',
'验证器集合',
'常量定义',
'API方法集合'
]
},
mixedExport: {
when: [
'有一个主要导出和多个辅助导出',
'主类配合相关工具和常量',
'需要向后兼容'
],
examples: [
'HTTP客户端 + 状态码',
'数据库类 + 查询构建器',
'组件 + 相关类型定义'
]
}
};
console.log('导出选择指南:', exportGuide);动态导入是ES2020引入的特性,支持运行时按需加载模块。
// 🎉 ES2020动态导入详解
console.log('=== 动态导入基础 ===');
// 1. 基本动态导入语法
async function loadMathModule() {
try {
// 动态导入返回Promise
const mathModule = await import('./math.js');
console.log('动态加载的模块:', mathModule);
console.log('PI值:', mathModule.PI);
console.log('加法结果:', mathModule.add(5, 3));
return mathModule;
} catch (error) {
console.error('模块加载失败:', error);
}
}
// 2. 条件动态导入
async function conditionalImport(feature) {
let module;
switch (feature) {
case 'charts':
module = await import('./charts.js');
break;
case 'analytics':
module = await import('./analytics.js');
break;
case 'export':
module = await import('./export.js');
break;
default:
throw new Error(`未知功能: ${feature}`);
}
return module;
}
// 使用条件导入
async function useConditionalImport() {
try {
const chartsModule = await conditionalImport('charts');
console.log('图表模块加载成功');
const analyticsModule = await conditionalImport('analytics');
console.log('分析模块加载成功');
} catch (error) {
console.error('条件导入失败:', error);
}
}
// 3. 用户交互触发的动态导入
console.log('=== 用户交互动态导入 ===');
class FeatureLoader {
constructor() {
this.loadedModules = new Map();
}
async loadFeature(featureName) {
// 检查是否已加载
if (this.loadedModules.has(featureName)) {
console.log(`功能 ${featureName} 已加载,直接返回`);
return this.loadedModules.get(featureName);
}
console.log(`开始加载功能: ${featureName}`);
try {
// 显示加载状态
this.showLoading(featureName);
// 动态导入模块
const module = await import(`./features/${featureName}.js`);
// 缓存模块
this.loadedModules.set(featureName, module);
// 隐藏加载状态
this.hideLoading(featureName);
console.log(`功能 ${featureName} 加载完成`);
return module;
} catch (error) {
this.hideLoading(featureName);
console.error(`功能 ${featureName} 加载失败:`, error);
throw error;
}
}
showLoading(featureName) {
console.log(`🔄 正在加载 ${featureName}...`);
}
hideLoading(featureName) {
console.log(`✅ ${featureName} 加载完成`);
}
}
const featureLoader = new FeatureLoader();
// 模拟用户点击事件
async function handleFeatureClick(featureName) {
try {
const module = await featureLoader.loadFeature(featureName);
// 使用加载的模块
if (module.default && typeof module.default.init === 'function') {
module.default.init();
}
} catch (error) {
console.error('功能加载失败:', error);
}
}
// 4. 路由懒加载
console.log('=== 路由懒加载 ===');
class Router {
constructor() {
this.routes = new Map();
this.currentRoute = null;
}
// 注册路由
register(path, moduleLoader) {
this.routes.set(path, {
loader: moduleLoader,
component: null
});
}
// 导航到路由
async navigate(path) {
if (!this.routes.has(path)) {
throw new Error(`路由不存在: ${path}`);
}
const route = this.routes.get(path);
// 如果组件未加载,则动态加载
if (!route.component) {
console.log(`懒加载路由组件: ${path}`);
try {
const module = await route.loader();
route.component = module.default || module;
console.log(`路由组件 ${path} 加载完成`);
} catch (error) {
console.error(`路由组件 ${path} 加载失败:`, error);
throw error;
}
}
// 渲染组件
this.renderComponent(route.component);
this.currentRoute = path;
}
renderComponent(component) {
console.log('渲染组件:', component.name || 'Anonymous');
if (typeof component.render === 'function') {
component.render();
}
}
}
// 使用路由懒加载
const router = new Router();
// 注册路由
router.register('/home', () => import('./pages/home.js'));
router.register('/about', () => import('./pages/about.js'));
router.register('/contact', () => import('./pages/contact.js'));
// 模拟路由导航
async function simulateNavigation() {
try {
await router.navigate('/home');
await router.navigate('/about');
await router.navigate('/home'); // 第二次访问,直接使用缓存
} catch (error) {
console.error('路由导航失败:', error);
}
}
// 5. 代码分割和性能优化
console.log('=== 代码分割优化 ===');
class ModuleManager {
constructor() {
this.cache = new Map();
this.loading = new Map();
}
async loadModule(modulePath, options = {}) {
const {
cache = true,
timeout = 10000,
retry = 3
} = options;
// 检查缓存
if (cache && this.cache.has(modulePath)) {
return this.cache.get(modulePath);
}
// 检查是否正在加载
if (this.loading.has(modulePath)) {
return this.loading.get(modulePath);
}
// 创建加载Promise
const loadPromise = this.loadWithRetry(modulePath, retry, timeout);
this.loading.set(modulePath, loadPromise);
try {
const module = await loadPromise;
// 缓存模块
if (cache) {
this.cache.set(modulePath, module);
}
return module;
} finally {
// 清理加载状态
this.loading.delete(modulePath);
}
}
async loadWithRetry(modulePath, maxRetries, timeout) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`尝试加载模块 ${modulePath} (第${attempt}次)`);
// 设置超时
const loadPromise = import(modulePath);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('加载超时')), timeout);
});
const module = await Promise.race([loadPromise, timeoutPromise]);
console.log(`模块 ${modulePath} 加载成功`);
return module;
} catch (error) {
lastError = error;
console.warn(`模块 ${modulePath} 加载失败 (第${attempt}次):`, error.message);
if (attempt < maxRetries) {
// 指数退避
const delay = Math.pow(2, attempt) * 1000;
console.log(`等待 ${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`模块 ${modulePath} 加载失败,已重试 ${maxRetries} 次: ${lastError.message}`);
}
// 预加载模块
preload(modulePaths) {
const preloadPromises = modulePaths.map(path => {
return this.loadModule(path).catch(error => {
console.warn(`预加载模块 ${path} 失败:`, error.message);
});
});
return Promise.allSettled(preloadPromises);
}
// 清理缓存
clearCache() {
this.cache.clear();
console.log('模块缓存已清理');
}
}
const moduleManager = new ModuleManager();
// 6. 实际应用示例
console.log('=== 实际应用示例 ===');
// 功能模块懒加载
class App {
constructor() {
this.moduleManager = new ModuleManager();
this.features = new Map();
}
async enableFeature(featureName) {
if (this.features.has(featureName)) {
console.log(`功能 ${featureName} 已启用`);
return this.features.get(featureName);
}
try {
console.log(`启用功能: ${featureName}`);
// 动态加载功能模块
const module = await this.moduleManager.loadModule(
`./features/${featureName}.js`,
{ timeout: 5000, retry: 2 }
);
// 初始化功能
const feature = new module.default();
await feature.init();
// 缓存功能实例
this.features.set(featureName, feature);
console.log(`功能 ${featureName} 启用成功`);
return feature;
} catch (error) {
console.error(`功能 ${featureName} 启用失败:`, error);
throw error;
}
}
async preloadCriticalFeatures() {
const criticalFeatures = [
'./features/auth.js',
'./features/navigation.js',
'./features/notifications.js'
];
console.log('预加载关键功能...');
await this.moduleManager.preload(criticalFeatures);
console.log('关键功能预加载完成');
}
}
// 使用示例
const app = new App();
async function runApp() {
// 预加载关键功能
await app.preloadCriticalFeatures();
// 按需启用功能
try {
await app.enableFeature('dashboard');
await app.enableFeature('reports');
} catch (error) {
console.error('应用启动失败:', error);
}
}通过本节ES6模块化教程的学习,你已经掌握:
A: ES6模块是静态的,支持tree-shaking和静态分析;CommonJS是动态的,运行时加载。ES6模块使用import/export,CommonJS使用require/module.exports。
A: 当模块只有一个主要功能时使用默认导出;当模块有多个相关功能时使用命名导出。也可以混合使用:主要功能默认导出,辅助功能命名导出。
A: 动态导入会产生网络请求,有一定延迟。但它能减少初始包大小,提升首屏加载速度。可以通过预加载、缓存等策略优化性能。
A: 使用try-catch包装await import(),或者使用.catch()处理Promise拒绝。建议实现重试机制和降级方案。
A: 现代浏览器和Node.js都支持ES6模块。对于旧环境,可以使用Babel等工具转译,或使用构建工具处理。
"掌握ES6模块化是现代JavaScript开发的基础技能。通过合理的模块组织和动态导入,不仅能提升代码的可维护性,还能显著优化应用性能。在实际项目中灵活运用这些技术,让你的代码更模块化、更高效!"