阿里云主机折上折
  • 微信号
Current Site:Index > The CommonJS module system translates this sentence into English.

The CommonJS module system translates this sentence into English.

Author:Chuan Chen 阅读数:10407人阅读 分类: Node.js

The CommonJS module system is one of the core mechanisms in Node.js for organizing and managing code. It implements module importing and exporting through require and module.exports, addressing the lack of native modular support in early JavaScript.

Basic Concepts of the CommonJS Module System

The CommonJS specification was initially designed for server-side JavaScript environments and was later adopted by Node.js as the foundation of its module system. In Node.js, each file is treated as an independent module, where variables, functions, and classes within the module are private by default and can only be accessed by other modules through explicit exports.

Key features of the module system include:

  • Synchronous module loading
  • Module caching mechanism
  • Clear dependency relationships
  • Simple import/export syntax

Module Export Mechanism

CommonJS provides two primary ways to export module content:

Using module.exports

This is the most basic export method, allowing the export of any type of value:

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

module.exports = {
  add,
  subtract
};

It can also directly export a single function or class:

// logger.js
module.exports = function(message) {
  console.log(`[LOG] ${new Date().toISOString()}: ${message}`);
};

Using the exports Shortcut

Node.js provides exports as a shortcut for module.exports:

// utils.js
exports.capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);
exports.trim = str => str.trim();

Note that exports is merely a reference to module.exports and cannot be directly reassigned:

// Incorrect usage
exports = { foo: 'bar' }; // This won't work

// Correct usage
module.exports = { foo: 'bar' };

Module Import Mechanism

Use the require function to import modules:

// app.js
const math = require('./math');
const logger = require('./logger');
const { capitalize } = require('./utils');

console.log(math.add(2, 3)); // 5
logger('Application started');
console.log(capitalize('hello')); // 'Hello'

require Lookup Rules

Node.js follows this order when searching for modules:

  1. Core modules (e.g., fs, path, etc.)
  2. File modules (starting with ./ or ../)
  3. Directory modules (looking for package.json or index.js)
  4. Modules in node_modules
// Import core module
const fs = require('fs');

// Import file module
const config = require('./config.json');

// Import directory module (automatically looks for index.js)
const models = require('./models');

// Import module from node_modules
const express = require('express');

Module Caching Mechanism

Node.js caches loaded modules to improve performance:

// moduleA.js
console.log('Module A loaded');
module.exports = { value: Math.random() };

// main.js
const a1 = require('./moduleA');
const a2 = require('./moduleA');

console.log(a1 === a2); // true
console.log(a1.value === a2.value); // true

Handling Circular Dependencies

CommonJS can handle circular dependencies between modules, but the timing of exports must be considered:

// a.js
console.log('a starting');
exports.done = false;
const b = require('./b');
console.log('in a, b.done =', b.done);
exports.done = true;
console.log('a done');

// b.js
console.log('b starting');
exports.done = false;
const a = require('./a');
console.log('in b, a.done =', a.done);
exports.done = true;
console.log('b done');

// main.js
console.log('main starting');
const a = require('./a');
const b = require('./b');
console.log('in main, a.done=', a.done, 'b.done=', b.done);

The execution result will show the order of module loading and state changes.

Module Scope

Each module has its own independent scope and does not pollute the global scope:

// module1.js
var count = 0;
function increment() {
  count++;
}

// module2.js
var count = 100; // Does not affect the count in module1

Dynamic Loading

Although CommonJS primarily loads modules synchronously, dynamic loading can also be implemented:

// Dynamic loading example
const moduleName = process.env.NODE_ENV === 'production' 
  ? './prodModule' 
  : './devModule';

const dynamicModule = require(moduleName);

Differences from ES Modules

Although Node.js now supports ES modules, CommonJS remains the primary module system:

  1. CommonJS loads synchronously, while ES modules load asynchronously.
  2. CommonJS uses require/module.exports, while ES modules use import/export.
  3. CommonJS modules are loaded at runtime, while ES modules are statically resolved at compile time.
// CommonJS
module.exports = { foo: 'bar' };
const lib = require('./lib');

// ES Modules
export const foo = 'bar';
import { foo } from './lib.mjs';

Practical Application Examples

Configuration File Management

// config.js
const env = process.env.NODE_ENV || 'development';

const configs = {
  development: {
    apiUrl: 'http://localhost:3000',
    debug: true
  },
  production: {
    apiUrl: 'https://api.example.com',
    debug: false
  }
};

module.exports = configs[env];

// app.js
const config = require('./config');
console.log(`API URL: ${config.apiUrl}`);

Middleware Pattern

// middleware.js
module.exports = function(req, res, next) {
  console.log(`${req.method} ${req.url}`);
  next();
};

// server.js
const express = require('express');
const logger = require('./middleware');

const app = express();
app.use(logger);

Module Hot Replacement

Although CommonJS does not natively support hot replacement, it can be achieved with some tricks:

// hotModule.js
module.exports = {
  data: 'initial'
};

// watcher.js
const fs = require('fs');
const path = require('path');

function watchModule(modulePath, callback) {
  const fullPath = require.resolve(modulePath);
  fs.watch(fullPath, () => {
    // Clear cache
    delete require.cache[fullPath];
    const newModule = require(modulePath);
    callback(newModule);
  });
}

// Usage example
watchModule('./hotModule', (module) => {
  console.log('Module updated:', module.data);
});

Performance Optimization Tips

  1. Organize module structure reasonably to avoid deep dependency chains.
  2. For frequently used core modules, cache them in advance:
const _require = require;
const path = _require('path');
const fs = _require('fs');
  1. Avoid dynamic require in hot paths.

Debugging the Module System

You can view module information through the module global object:

console.log(module);

// View module cache
console.log(require.cache);

Module Pattern Practices

CommonJS supports various module organization patterns:

Singleton Pattern

// db.js
let instance = null;

class Database {
  constructor() {
    if (!instance) {
      instance = this;
      this.connection = null;
    }
    return instance;
  }
  
  connect() {
    this.connection = 'Connected';
  }
}

module.exports = new Database();

// app.js
const db1 = require('./db');
const db2 = require('./db');

console.log(db1 === db2); // true

Factory Pattern

// logger.js
module.exports = (prefix) => {
  return {
    log: message => console.log(`[${prefix}] ${message}`),
    error: message => console.error(`[${prefix}] ERROR: ${message}`)
  };
};

// app.js
const createLogger = require('./logger');
const dbLogger = createLogger('DB');
const apiLogger = createLogger('API');

dbLogger.log('Connection established');
apiLogger.error('Request failed');

Module Version Management

In large projects, managing different module versions may be necessary:

// v1/module.js
module.exports = function() {
  console.log('This is version 1');
};

// v2/module.js
module.exports = function() {
  console.log('This is version 2');
};

// app.js
const v1 = require('./v1/module');
const v2 = require('./v2/module');

v1(); // This is version 1
v2(); // This is version 2

Module Testing Techniques

When testing CommonJS modules, you can leverage the caching mechanism:

// counter.js
let count = 0;

module.exports = {
  increment: () => ++count,
  getCount: () => count
};

// test.js
const counter = require('./counter');

// Reset module state before each test case
beforeEach(() => {
  delete require.cache[require.resolve('./counter')];
});

test('increment should increase count', () => {
  const counter = require('./counter');
  counter.increment();
  expect(counter.getCount()).toBe(1);
});

Module Metadata

You can access module metadata through the module object:

console.log('Module ID:', module.id);
console.log('Module filename:', module.filename);
console.log('Module loaded:', module.loaded);
console.log('Parent module:', module.parent);
console.log('Children modules:', module.children);

Module Path Resolution

Node.js provides utility methods for module path resolution:

const resolvedPath = require.resolve('./someModule');
console.log('Resolved path:', resolvedPath);

Module Wrapper

Node.js wraps module code in a function before execution:

(function(exports, require, module, __filename, __dirname) {
  // Module code is executed here
});

This explains why these variables are available in modules:

console.log('File:', __filename);
console.log('Directory:', __dirname);

Module Loading Process

Detailed steps of Node.js module loading:

  1. Resolve module path
  2. Check cache
  3. Create new module instance
  4. Store module instance in cache
  5. Load module content
  6. Wrap module code
  7. Execute module code
  8. Return module.exports

Relationship Between Modules and Packages

In the Node.js ecosystem:

  • A module is a single file
  • A package is a directory containing package.json
  • A package can contain multiple modules
// Import main module from package
const pkg = require('some-package');

// Import specific module from package
const util = require('some-package/utils');

Module System Extensions

Although CommonJS is Node.js's default module system, it can be extended through loaders:

// Custom JSON loader
require.extensions['.json'] = function(module, filename) {
  const content = fs.readFileSync(filename, 'utf8');
  module.exports = JSON.parse(content);
};

// Now JSON files can be directly required
const data = require('./data.json');

Note: Modifying require.extensions has been deprecated by the official documentation and is not recommended for production environments.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.