Why Module System is Needed?

In Node.js, we can split code into multiple independent files, each acting as a “module”. The benefits of this approach are:
- Code Reusability: Different files can share functionality (e.g., utility functions, configuration).
- Code Organization: Separate different functional code into distinct files, making maintenance and navigation easier.
- Avoid Global Pollution: Variables and functions inside a module are private by default, preventing interference with other files.

I. What is a Module in Node.js?

Every .js file is an independent module. For example, creating a utils.js file makes it a module. Variables, functions, classes, etc., inside a module are by default “private”—only accessible externally through the export mechanism.

II. How to Export Module Content (exports)?

To make a module’s content accessible to other modules, use the exports object. exports is a default empty object; you can attach properties or methods to it.

1. Export a Single Value

// utils.js
exports.PI = 3.14159; // Export a constant
exports.add = (a, b) => a + b; // Export a function

2. Export Multiple Values

For multiple properties/methods, add them directly to the exports object:

// utils.js
exports.name = "Node.js";
exports.version = "18.0.0";

exports.log = (msg) => console.log("Log:", msg);

3. Export an Entire Object

For complex objects (e.g., configurations, classes), assign the object directly to exports:

// config.js
const config = {
  port: 3000,
  db: "mysql",
  secret: "abc123"
};

module.exports = config; // Directly export the object (note: exports = config also works, but module.exports is preferred)

III. How to Import Module Content (require)?

To use another module’s content, use the require function, which returns the exported content of the module.

1. Import Local Modules

Import modules from the current project using relative paths (starting with ./ or ../):

// main.js
const utils = require("./utils.js"); // Import utils.js from current directory
const config = require("./config.js"); // Import config.js from current directory

// Use exported content
console.log(utils.PI); // 3.14159
console.log(utils.add(1, 2)); // 3
console.log(config.port); // 3000

2. Import Third-Party Modules

For Node.js packages (e.g., express, lodash), use the package name directly:

const express = require("express"); // Import express module
const _ = require("lodash"); // Import lodash module

IV. Key Detail: Relationship Between exports and module.exports

  • exports is a reference to module.exports (i.e., exports = module.exports), initially pointing to an empty object.
  • You can attach properties via exports (e.g., exports.xxx = ...) or directly assign to module.exports (e.g., module.exports = { ... }).
  • Warning: Directly assigning to exports (e.g., exports = { a: 1 }) may override the default module.exports. Thus, using exports.xxx or module.exports is recommended.

V. Comprehensive Example

Suppose we have two files: math.js (exports math utilities) and main.js (imports and uses them).

1. Export Module (math.js)

// math.js
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
exports.multiply = (a, b) => a * b;

2. Import Module (main.js)

// main.js
const math = require("./math.js"); // Import math.js

// Use exported functions
console.log("1+2=", math.add(1, 2)); // Output: 1+2=3
console.log("5-3=", math.subtract(5, 3)); // Output: 5-3=2
console.log("2*4=", math.multiply(2, 4)); // Output: 2*4=8

3. Run Result

Execute node main.js in the terminal to see the output.

Summary

  • Module: Each .js file is a module with private internal content by default.
  • Export: Expose content via exports or module.exports.
  • Import: Use require to access other modules’ exported content.

Mastering exports and require is foundational for Node.js development, as they are heavily used in frameworks (e.g., Express) and tooling (e.g., Webpack). Practice exporting/importing modules to improve code organization!

Xiaoye