1. 什么是模块化

模块化就是把一个大文件拆成多个小文件,每个文件负责一块功能,文件之间可以互相引用。

想象一个超市:把"日用品区"、"食品区"、"文具区"分开放,而不是把所有商品堆在一起。模块化就是给代码"分区"。

为什么要模块化?

问题 vs 解决:
问题(没有模块化) 解决(有了模块化)
所有代码写在一个文件里,几千行 拆成多个文件,每个几百行
变量名冲突(都是全局的) 每个模块有自己的作用域
想用别人写的函数,得手动拷贝 import 引入就行
不知道哪个函数在哪 按功能组织,一目了然

ES 模块的使用方式:

在 HTML 中,使用 <script type="module"> 来加载模块。加了 type="module" 后,浏览器就知道这个 <script> 里会用 import/export。模块文件默认是延迟执行的(相当于加了 defer)。

1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <title>模块化示例</title> 6</head> 7<body> 8 <h1>模块化入门</h1> 9 <!-- type="module" 让浏览器识别 import/export 语法 --> 10 <script type="module" src="main.js"></script> 11</body> 12</html>

2. 命名导出与导入

命名导出:给每个要导出的内容起个名字,导入时必须用相同的名字来接收。

导出(export)——两种方式:

utils.js — 命名导出:
// 方式1:声明时直接导出
export function add(a, b) {
    return a + b;
}

export function multiply(a, b) {
    return a * b;
}

export const PI = 3.14159;

// 方式2:先声明,最后统一导出
function subtract(a, b) {
    return a - b;
}

const VERSION = '1.0.0';

export { subtract, VERSION };

导入(import)——三种方式:

main.js — 命名导入:
// 导入指定的几个
import { add, multiply, PI } from './utils.js';
console.log(add(10, 5));        // 15
console.log(multiply(3, 4));    // 12
console.log(PI);                // 3.14159

// 导入时重命名(避免命名冲突)
import { add as sum } from './utils.js';
console.log(sum(10, 5));        // 15

// 导入全部
import * as utils from './utils.js';
console.log(utils.add(1, 2));   // 3
console.log(utils.PI);          // 3.14159

完整的可运行示例(需要本地服务器,浏览器不允许 file:// 协议加载模块):

命名导出演示(内联模块):
控制台输出:
【JavaScript入门】
这是一段很长很长的文章摘...
标题最大长度:50
1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <title>命名导出示例</title> 6</head> 7<body> 8 <h1>命名导出与导入</h1> 9 <p>打开控制台查看结果</p> 10 11 <!-- 内联模块示例 --> 12 <script type="module"> 13 // 模拟 utils.js 的内容 14 15 // 命名导出 16 function formatTitle(title) { 17 return '【' + title + '】'; 18 } 19 20 function truncate(text, maxLength) { 21 if (text.length > maxLength) { 22 return text.slice(0, maxLength) + '...'; 23 } 24 return text; 25 } 26 27 const MAX_TITLE_LENGTH = 50; 28 29 // 模拟导入使用 30 let title = formatTitle('JavaScript入门'); 31 console.log(title); // 【JavaScript入门】 32 33 let summary = truncate('这是一段很长很长的文章摘要,需要被截断显示', 10); 34 console.log(summary); // 这是一段很长很长的文章摘... 35 36 console.log('标题最大长度:' + MAX_TITLE_LENGTH); 37 </script> 38</body> 39</html>

命名导出/导入速查表:

速查表:
操作 语法 说明
声明时导出 export function fn() {} 声明的同时导出
统一导出 export { fn, NAME } 最后统一列出
导入指定 import { fn } from './a.js' 名字要对应
重命名导入 import { fn as myFn } from './a.js' 避免冲突
导入全部 import * as obj from './a.js' 整体导入

⚠️ 新手易错import { add } from 'utils.js' 会报错!路径必须写 ./utils.js(相对路径),不能省略 ./

3. 默认导出与导入

默认导出:一个模块只能有一个默认导出。导入时可以自己起名字

默认导出适合一个模块只导出一个主要功能(比如一个类、一个函数、一个组件)。

导出(export default):

logger.js — 默认导出:
function logMessage(message) {
    console.log('[LOG]', new Date().toLocaleTimeString(), message);
}

export default logMessage;   // 默认导出,一个模块只能有一个

导入(import):

main.js — 默认导入(名字可以随便起):
import log from './logger.js';   // 不需要花括号,名字随便起

log('服务器启动了');   // [LOG] 10:30:00 服务器启动了

完整的可运行示例:

默认导出演示:
控制台输出:
《ES6模块化》— 2026/6/12
{ title: 'ES6模块化', content: '模块化让代码更清晰...', date: '2026/6/12' }
1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <title>默认导出示例</title> 6</head> 7<body> 8 <h1>默认导出与导入</h1> 9 <script type="module"> 10 // 模拟 defaultExport.js 11 12 // 默认导出一个函数 13 function createArticle(title, content) { 14 return { 15 title, 16 content, 17 date: new Date().toLocaleDateString(), 18 toString() { 19 return `《${this.title}》— ${this.date}`; 20 } 21 }; 22 } 23 24 // 模拟导入使用(名字可以随便起) 25 let article = createArticle('ES6模块化', '模块化让代码更清晰...'); 26 console.log(article.toString()); 27 console.log(article); 28 </script> 29</body> 30</html>

命名导出 vs 默认导出对比:

对比表格:
特性 命名导出 默认导出
每个模块数量 多个 只能一个
导入语法 import { name } from '...' import anyName from '...'
导入时名字 必须和导出名一致 可以随便起
使用场景 导出多个工具函数/常量 导出一个主要功能

⚠️ 新手易错:默认导入不需要 {}import log from '...' ✅,import { log } from '...' ❌(这是命名导入,名字必须匹配)。

4. 混合使用

在实际项目中,一个模块经常既有命名导出,又有默认导出

api.js — 混合导出:
// 默认导出(主要功能)
export default function fetchPosts() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve([{ title: '文章1' }, { title: '文章2' }]);
        }, 500);
    });
}

// 命名导出(辅助功能)
export const API_BASE_URL = 'https://api.example.com';

export function formatDate(date) {
    return new Date(date).toLocaleDateString('zh-CN');
}
main.js — 混合导入:
// 默认导入(名字随便起)
import fetchPosts from './api.js';

// 命名导入(名字必须对应)
import { API_BASE_URL, formatDate } from './api.js';

// 也可以一行搞定
import fetchArticles, { API_BASE_URL, formatDate } from './api.js';

console.log(API_BASE_URL);     // https://api.example.com
console.log(formatDate('2026-06-12'));  // 2026/6/12

fetchPosts().then(posts => console.log(posts));

完整的可运行示例(两个模块互相引用):

混合导出演示:
控制台输出:
计算结果:30
{ title: '模块化真好', content: '代码拆分成多个文件...', formatted: '模块化真好\n---\n代码拆分成多个文件...', wordCount: 11 }
博客名:我的博客
1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <title>混合导出示例</title> 6</head> 7<body> 8 <h1>混合使用:两个模块互相引用</h1> 9 <script type="module"> 10 // ========== 模拟 math.js ========== 11 // 命名导出 12 function add(a, b) { return a + b; } 13 function multiply(a, b) { return a * b; } 14 // 默认导出 15 function calculate(expr) { 16 let parts = expr.split('+'); 17 return add(Number(parts[0]), Number(parts[1])); 18 } 19 20 // ========== 模拟 blog.js ========== 21 // 命名导出 22 const BLOG_NAME = '我的博客'; 23 function formatPost(title, content) { 24 return `${title}\n---\n${content}`; 25 } 26 // 默认导出 27 function createPost(title, content) { 28 return { 29 title, 30 content, 31 formatted: formatPost(title, content), 32 wordCount: content.length 33 }; 34 } 35 36 // ========== 使用 ========== 37 let result = calculate('10+20'); 38 console.log('计算结果:' + result); // 30 39 40 let post = createPost('模块化真好', '代码拆分成多个文件...'); 41 console.log(post); 42 console.log('博客名:' + BLOG_NAME); // 我的博客 43 </script> 44</body> 45</html>

5. 模块化最佳实践

推荐的文件组织方式:

项目结构:
src/ ├── utils/ 工具函数(命名导出为主) │ ├── format.js formatTitle, formatDate, formatPrice │ ├── validate.js isEmail, isPhone, isRequired │ └── index.js 统一导出所有工具 ├── api/ API 请求(默认导出为主) │ ├── posts.js export default fetchPosts │ └── users.js export default fetchUsers └── main.js 入口文件,统一导入使用

统一导出的写法utils/index.js):

utils/index.js — 统一把所有工具导出:
// utils/index.js
export { formatTitle, formatDate, formatPrice } from './format.js';
export { isEmail, isPhone, isRequired } from './validate.js';
main.js — 一行导入所有工具:
import { formatTitle, isEmail } from './utils/index.js';

模块化原则:

原则速查:
原则 说明
一个文件一个职责 format.js 只管格式化,validate.js 只管验证
默认导出用于主要功能 一个模块的"主角"用默认导出
命名导出用于辅助功能 工具函数、常量用命名导出
统一入口 index.js 多个文件通过 index.js 统一对外暴露

实践作业:将数组高阶方法的处理函数拆分为独立模块。

  • 创建 postUtils.js 模块,命名导出:getPopularPostsgetPostTitlesgetTotalViewsfindPostById
  • 创建 formatUtils.js 模块,命名导出:formatDatetruncateText
  • main.js 中导入这两个模块,处理文章数据
实践作业演示:
控制台输出:
热门文章: ['JavaScript入门', 'React教程']
总浏览量:5250
找到文章:{ id: 2, title: 'CSS布局技巧', ... }
JavaScript入门(2026/6/1)— 零基础学习Jav...
CSS布局技巧(2026/6/5)— Flex和Grid布...
React教程(2026/6/10)— 从零开始学习R...
1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <title>实践作业</title> 6</head> 7<body> 8 <h1>实践作业:模块化拆分</h1> 9 <script type="module"> 10 // ========== postUtils.js ========== 11 function getPopularPosts(posts, minViews) { 12 return posts.filter(post => post.views >= minViews); 13 } 14 15 function getPostTitles(posts) { 16 return posts.map(post => post.title); 17 } 18 19 function getTotalViews(posts) { 20 return posts.reduce((sum, post) => sum + post.views, 0); 21 } 22 23 function findPostById(posts, id) { 24 return posts.find(post => post.id === id); 25 } 26 27 // ========== formatUtils.js ========== 28 function formatDate(date) { 29 return new Date(date).toLocaleDateString('zh-CN'); 30 } 31 32 function truncateText(text, maxLength) { 33 if (text.length <= maxLength) return text; 34 return text.slice(0, maxLength) + '...'; 35 } 36 37 // ========== main.js ========== 38 let posts = [ 39 { id: 1, title: 'JavaScript入门', views: 1200, date: '2026-06-01', 40 summary: '零基础学习JavaScript的基础语法' }, 41 { id: 2, title: 'CSS布局技巧', views: 850, date: '2026-06-05', 42 summary: 'Flex和Grid布局完全指南' }, 43 { id: 3, title: 'React教程', views: 3200, date: '2026-06-10', 44 summary: '从零开始学习React框架' } 45 ]; 46 47 // 使用 postUtils 的函数 48 let popular = getPopularPosts(posts, 1000); 49 console.log('热门文章:', getPostTitles(popular)); 50 console.log('总浏览量:' + getTotalViews(posts)); 51 52 let found = findPostById(posts, 2); 53 console.log('找到文章:', found); 54 55 // 使用 formatUtils 的函数 56 posts.forEach(post => { 57 let shortSummary = truncateText(post.summary, 8); 58 console.log(`${post.title}(${formatDate(post.date)})— ${shortSummary}`); 59 }); 60 </script> 61</body> 62</html>

本章复盘——模块化核心要点:

核心要点总结:
要点 说明
为什么模块化 拆分代码、避免全局污染、方便复用和维护
命名导出 export { fn, NAME },导入名字必须一致
默认导出 export default fn,一个模块一个,导入名字随便起
混合导出 可以同时有默认导出和命名导出
HTML 使用 <script type="module">
路径规则 必须用相对路径 ./ 或绝对路径
组织原则 一个文件一个职责,默认导出主要功能,命名导出辅助