模块化概述

什么是模块化

模块化是将程序拆分为独立模块的开发方式。

模块化的意义

// 模块化的目的:
// 1. 代码组织:将相关功能组织在一起
// 2. 命名空间:避免全局变量污染
// 3. 复用性:模块可以在不同项目中复用
// 4. 可维护性:独立模块更易维护
// 5. 依赖管理:明确模块间的依赖关系

// 无模块化的问题
// 全局污染
var name = "东巴文";
var name = "db-w.cn";  // 覆盖

// 命名冲突
function getData() {}
function getData() {}  // 覆盖

// 依赖混乱
// 谁依赖谁?加载顺序?

模块化的演变

// 阶段1:全局函数
function foo() {}
function bar() {}

// 阶段2:命名空间
var MyApp = {
    foo: function() {},
    bar: function() {}
};

// 阶段3:IIFE
var Module = (function() {
    var private = "私有";
    
    function privateMethod() {}
    
    return {
        public: "公开",
        publicMethod: function() {}
    };
})();

// 阶段4:模块规范
// CommonJS, AMD, UMD, ES Modules

模块化规范

主要的模块化规范。

CommonJS

// Node.js使用的规范
// 导出
module.exports = {
    name: "东巴文",
    greet: function() {
        return "Hello";
    }
};

// 或
exports.name = "东巴文";
exports.greet = function() {
    return "Hello";
};

// 导入
const module = require("./module");
console.log(module.name);

AMD

// 异步模块定义(浏览器端)
define(["jquery", "underscore"], function($, _) {
    return {
        name: "东巴文",
        init: function() {
            $("body").html("Hello");
        }
    };
});

// 使用
require(["module"], function(module) {
    module.init();
});

UMD

// 通用模块定义
(function(root, factory) {
    if (typeof define === "function" && define.amd) {
        // AMD
        define([], factory);
    } else if (typeof module === "object" && module.exports) {
        // CommonJS
        module.exports = factory();
    } else {
        // 全局变量
        root.MyModule = factory();
    }
}(typeof self !== "undefined" ? self : this, function() {
    return {
        name: "东巴文",
        greet: function() {
            return "Hello";
        }
    };
}));

ES Modules

// ES6模块
// 导出
export const name = "东巴文";
export function greet() {
    return "Hello";
}
export default { name, greet };

// 导入
import { name, greet } from "./module.js";
import myModule from "./module.js";
import * as all from "./module.js";

模块化优势

模块化带来的好处。

代码组织

// 按功能组织代码
// utils.js
export function formatDate(date) {
    return date.toLocaleDateString();
}

export function debounce(fn, delay) {
    let timer;
    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

// api.js
import { request } from "./request.js";

export function getUser(id) {
    return request(`/api/user/${id}`);
}

export function getPosts(userId) {
    return request(`/api/posts/${userId}`);
}

// app.js
import { formatDate } from "./utils.js";
import { getUser, getPosts } from "./api.js";

async function init() {
    const user = await getUser(1);
    console.log(formatDate(new Date()));
}

作用域隔离

// 模块有独立的作用域
// moduleA.js
const name = "东巴文";
export function getName() {
    return name;
}

// moduleB.js
const name = "db-w.cn";  // 不冲突
export function getName() {
    return name;
}

// app.js
import { getName as getNameA } from "./moduleA.js";
import { getName as getNameB } from "./moduleB.js";

console.log(getNameA());  // 东巴文
console.log(getNameB());  // db-w.cn

依赖管理

// 明确的依赖关系
// userService.js
import { api } from "./api.js";
import { cache } from "./cache.js";

export class UserService {
    async getUser(id) {
        const cached = cache.get(`user:${id}`);
        if (cached) return cached;
        
        const user = await api.get(`/users/${id}`);
        cache.set(`user:${id}`, user);
        return user;
    }
}

// 依赖图
// app.js
//   ├── userService.js
//   │     ├── api.js
//   │     └── cache.js
//   └── router.js

模块设计原则

设计模块的最佳实践。

单一职责

// 好的设计:每个模块只做一件事
// formatDate.js
export function formatDate(date) {
    return date.toLocaleDateString();
}

// parseDate.js
export function parseDate(str) {
    return new Date(str);
}

// 不好的设计:一个模块做太多事
// dateUtils.js
export function formatDate() {}
export function parseDate() {}
export function calculateAge() {}
export function getDaysBetween() {}

高内聚低耦合

// 高内聚:相关功能放在一起
// userValidator.js
export function validateName(name) {
    return name.length >= 2;
}

export function validateEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

export function validateAge(age) {
    return age >= 0 && age <= 150;
}

// 低耦合:模块间依赖最小化
// 使用依赖注入
export class UserService {
    constructor(api) {
        this.api = api;
    }
    
    async getUser(id) {
        return this.api.get(`/users/${id}`);
    }
}

接口设计

// 提供清晰的公共接口
// calculator.js

// 私有函数
function validateInput(a, b) {
    if (typeof a !== "number" || typeof b !== "number") {
        throw new TypeError("参数必须是数字");
    }
}

// 公共接口
export function add(a, b) {
    validateInput(a, b);
    return a + b;
}

export function subtract(a, b) {
    validateInput(a, b);
    return a - b;
}

// 默认导出
export default {
    add,
    subtract
};

模块化工具

常用的模块化构建工具。

Webpack

// webpack.config.js
module.exports = {
    entry: "./src/index.js",
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "dist")
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: "babel-loader"
            }
        ]
    }
};

Rollup

// rollup.config.js
export default {
    input: "src/index.js",
    output: {
        file: "dist/bundle.js",
        format: "esm"
    }
};

Vite

// vite.config.js
import { defineConfig } from "vite";

export default defineConfig({
    root: "src",
    build: {
        outDir: "../dist"
    }
});

下一步

掌握了模块化概述后,让我们继续学习:

  1. CommonJS - 学习CommonJS规范
  2. [ES Modules](./59_ES Modules.md) - 学习ES模块
  3. 模块打包工具 - 学习打包工具

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:模块化是现代JavaScript开发的基石,理解模块化的演变和各种规范有助于更好地组织代码。在 db-w.cn,我们帮你建立模块化思维!