白骑士的JavaScript教学高级篇之模块化 4.1.3 CommonJS模块(require, module.exports)

        在深入了解ES6模块化系统之前,JavaScript社区主要使用CommonJS作为模块化方案。CommonJS广泛应用于Node.js环境,它通过‘require‘和‘module.exports‘提供了简单而有效的模块化机制。虽然ES6模块已经成为现代JavaScript的主流,但理解CommonJS依然非常重要,特别是在处理旧版代码或Node.js项目时。

什么是CommonJS模块?

        CommonJS是一种同步模块化规范,主要用于服务器端的JavaScript开发,尤其是在Node.js环境中。与ES6模块不同,CommonJS模块是在运行时加载的,这意味着在代码执行时,模块的导入和导出是动态进行的。

同步加载

        CommonJS模块是同步加载的,这在服务器端非常合适,因为所有的模块都可以在脚本启动时一次性加载完毕。然而,这种同步加载在浏览器环境中可能会导致性能问题,因为网络请求是异步的,可能会阻塞页面的渲染。

单文件模块

        每个CommonJS模块都是一个单独的文件,并且该文件中的所有内容都是私有的,除非明确地导出。这使得模块可以在不影响全局作用域的情况下独立运行。

‘module.exports‘:导出模块内容

        在CommonJS模块系统中,‘module.exports‘用于定义模块对外暴露的内容。导出的内容可以是对象、函数、类,甚至是基本数据类型。

// math.js
const add = (x, y) => x + y;
const subtract = (x, y) => x - y;

module.exports = {
    add,
    subtract
};

        在上面的例子中,‘math.js‘模块导出一个包含‘add‘和‘subtract‘函数的对象。其他模块可以通过‘require‘来使用这些导出的函数。

导出单一内容

        如果你希望导出单一的函数或对象,而不是包含多个属性的对象,可以直接赋值给‘module.exports‘。

// calculator.js
module.exports = class Calculator {
    constructor() {
        this.result = 0;
    }

    add(x) {
        this.result += x;
        return this.result;
    }

    subtract(x) {
        this.result -= x;
        return this.result;
    }
};

        在这个例子中,整个‘Calculator‘类被导出,导入时可以直接引用该类。

‘exports‘的简写

        ‘exports‘是‘module.exports‘的一个简写,通常用于简化导出多个属性的场景。

// math.js
exports.add = (x, y) => x + y;
exports.subtract = (x, y) => x - y;

        需要注意的是,‘exports‘只是‘module.exports‘的一个引用,因此直接赋值给‘exports‘会断开它与‘module.exports‘的联系,从而导致导出失效。

// 不推荐
exports = {
    add,
    subtract
};

// 推荐
module.exports = {
    add,
    subtract
};

‘require‘:导入模块内容

        ‘require‘是CommonJS模块系统中用于导入其他模块的关键字。它可以从文件、核心模块或第三方模块中加载导出的内容。

导入自定义模块

        要导入自定义的模块,可以通过‘require‘指定模块文件的路径。需要注意,路径必须以‘./‘或‘../‘开头。

// main.js
const math = require('./math.js');

console.log(math.add(2, 3)); // 输出:5
console.log(math.subtract(5, 2)); // 输出:3

        在这个例子中,我们从‘math.js‘模块中导入了‘add‘和‘subtract‘函数,并在‘main.js‘中使用它们。

导入Node.js核心模块

        Node.js自带了一些核心模块,这些模块无需安装,可以直接通过‘require‘加载。例如,‘fs‘模块用于文件系统操作。

const fs = require('fs');

fs.writeFileSync('hello.txt', 'Hello, World!');

        在这个例子中,‘fs‘模块被导入,用于创建一个新的文本文件‘hello.txt‘并写入内容。

导入第三方模块

        通过‘npm‘(Node Package Manager)安装的第三方模块,也可以通过‘require‘导入。例如,流行的‘lodash‘库可以通过以下方式导入和使用:

const _ = require('lodash');

const arr = [1, 2, 3, 4];
console.log(_.reverse(arr)); // 输出:[4, 3, 2, 1]

        只需安装‘lodash‘模块后,它就可以在代码中使用。

CommonJS模块的特性

        CommonJS模块化系统具有一些独特的特性,使其在Node.js环境中非常流行。

单例模式

        CommonJS模块在第一次被‘require‘时执行并缓存,其后每次‘require‘都会返回同一个模块实例。这种单例模式确保了模块的状态在整个应用中是一致的。

// counter.js
let count = 0;

module.exports = {
    increment() {
        count += 1;
        return count;
    },

    getCount() {
        return count;
    }
};

        即使在多个文件中多次导入‘counter.js‘,计数器的状态也会保持一致。

动态加载

        与ES6模块的静态分析不同,CommonJS模块是在运行时动态加载的。这意味着你可以根据条件动态决定是否加载某个模块。

if (someCondition) {
    const specialModule = require('./specialModule.js');
    specialModule.doSomething();
}

        这种灵活性使得CommonJS在处理特定场景时非常有用。

CommonJS与ES6模块的区别

        尽管CommonJS和ES6模块系统在本质上都是为了实现模块化,但它们之间有一些关键区别。

导入导出的方式

        CommonJS使用‘require‘和‘module.exports‘进行导入和导出,而ES6模块则使用‘import‘和‘export‘。这种差异不仅在语法上存在不同,也影响了模块的加载方式和时机。

同步 vs 异步

        CommonJS模块是同步加载的,这意味着在执行时必须等待模块加载完成。相比之下,ES6模块支持异步加载,可以在不阻塞代码执行的情况下并行加载多个模块。

使用环境

        CommonJS主要用于Node.js环境,而ES6模块设计初衷是为浏览器和Node.js提供统一的模块化方案。尽管Node.js现已支持ES6模块,但许多旧项目仍使用CommonJS。

静态 vs 动态

        ES6模块是静态分析的,这意味着在代码编译时就能确定模块的依赖关系。而CommonJS模块是在运行时动态加载的,这使得它们的依赖关系在执行时才确定。

总结

        CommonJS模块系统虽然已经被ES6模块部分取代,但它在Node.js开发中仍然占据着重要地位。理解并掌握CommonJS模块的工作原理,对于处理旧代码和维护现有项目至关重要。通过‘require‘和‘module.exports‘,你可以灵活地组织和管理JavaScript代码,使项目的结构更加清晰。无论是在学习ES6模块还是处理现代JavaScript项目时,对CommonJS的深刻理解都会为你提供坚实的基础。

猜你喜欢

转载自blog.csdn.net/JeremyTC/article/details/143270320