阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模块的分类(核心模块、文件模块、第三方模块)

模块的分类(核心模块、文件模块、第三方模块)

作者:陈川 阅读数:26019人阅读 分类: Node.js

在Node.js中,模块是代码组织和复用的基本单位。根据来源和使用方式的不同,Node.js模块可以分为核心模块、文件模块和第三方模块。每种模块类型具有不同的加载机制和应用场景,理解它们的区别对开发高效、可维护的应用程序至关重要。

核心模块

核心模块是Node.js内置的模块,由Node.js官方提供并随运行时一起安装。这些模块通常提供基础功能,如文件系统操作、网络通信、路径处理等。核心模块的加载速度最快,因为它们在Node.js进程启动时已被编译成二进制文件。

常见核心模块示例

  • fs: 文件系统操作

    const fs = require('fs');
    fs.readFile('/path/to/file', 'utf8', (err, data) => {
      if (err) throw err;
      console.log(data);
    });
    
  • http: 创建HTTP服务器

    const http = require('http');
    http.createServer((req, res) => {
      res.end('Hello World');
    }).listen(3000);
    
  • path: 路径处理

    const path = require('path');
    const fullPath = path.join(__dirname, 'files', 'test.txt');
    

核心模块特点

  1. 无需单独安装,直接通过require加载
  2. 加载时不需要指定路径(如require('fs')
  3. 性能最优,优先使用核心模块实现功能

文件模块

文件模块是开发者自定义的模块,通常存储在项目目录中。这类模块可以是单个JavaScript文件,也可以是一个包含index.js的目录。文件模块通过相对路径或绝对路径加载。

文件模块的创建与使用

1. 单个文件模块

// calculator.js
function add(a, b) {
  return a + b;
}
module.exports = { add };

// app.js
const calc = require('./calculator');
console.log(calc.add(2, 3)); // 输出5

2. 目录模块

/my-module
  ├── index.js
  ├── utils.js
  └── package.json
// my-module/index.js
const { helper } = require('./utils');
module.exports = {
  main: () => helper()
};

// app.js
const myModule = require('./my-module');

文件模块加载规则

  1. 精确文件名匹配:require('./module')会依次尝试:
    • module.js
    • module.json
    • module.node
  2. 目录加载:当require路径是目录时,查找:
    • 目录下的package.json中main字段指定的文件
    • 目录下的index.js
    • 目录下的index.json
    • 目录下的index.node

第三方模块

第三方模块是通过npm(Node Package Manager)安装的社区或组织提供的模块。这些模块通常存放在项目的node_modules目录中,可以解决特定领域问题或提供高级功能封装。

第三方模块使用流程

  1. 安装模块
npm install lodash
  1. 在代码中引用
const _ = require('lodash');
const users = [
  { 'user': 'barney', 'age': 36 },
  { 'user': 'fred',   'age': 40 }
];
const names = _.map(users, 'user'); // ['barney', 'fred']

常用第三方模块示例

  • express: Web应用框架

    const express = require('express');
    const app = express();
    app.get('/', (req, res) => res.send('Hello World'));
    app.listen(3000);
    
  • mongoose: MongoDB对象建模

    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/test');
    const Cat = mongoose.model('Cat', { name: String });
    const kitty = new Cat({ name: 'Zildjian' });
    kitty.save().then(() => console.log('meow'));
    
  • axios: HTTP客户端

    const axios = require('axios');
    axios.get('https://api.example.com/data')
      .then(response => console.log(response.data));
    

第三方模块特点

  1. 必须通过npm或yarn安装
  2. 加载时只需模块名,不需要路径(如require('lodash')
  3. 版本管理通过package.json实现
  4. 可以包含自己的依赖关系

模块加载机制

Node.js的模块系统采用CommonJS规范,其加载顺序和规则有特定逻辑:

  1. 优先检查是否是核心模块
  2. 如果不是核心模块,尝试从当前目录的node_modules查找
  3. 如果未找到,向父目录的node_modules查找,直到根目录
  4. 如果带路径(如./module),则按文件模块处理

模块缓存机制

Node.js会对加载过的模块进行缓存,后续require同一模块时直接返回缓存结果。可以通过require.cache查看所有缓存的模块。

console.log(require.cache);
// 输出类似:
// {
//   '/path/to/module.js': {
//     id: '/path/to/module.js',
//     exports: {},
//     ...
//   }
// }

模块作用域

每个模块都有独立的作用域,通过module.exportsexports暴露接口。模块内部的变量、函数默认都是私有的。

// module.js
const privateVar = 'secret';
function privateFunc() {
  console.log(privateVar);
}
module.exports = {
  publicFunc: () => privateFunc()
};

// app.js
const mod = require('./module');
mod.publicFunc(); // 输出"secret"
console.log(mod.privateVar); // undefined

ES模块与CommonJS

Node.js从v12开始稳定支持ES模块,两种模块系统可以共存:

CommonJS模块

// cjs-module.js
module.exports = { name: 'CJS' };

// app.js
const cjs = require('./cjs-module');

ES模块

// esm-module.mjs
export const name = 'ESM';

// app.mjs
import { name } from './esm-module.mjs';
console.log(name);

互操作性

  1. ES模块可以导入CommonJS模块

    // esm-app.mjs
    import cjs from './cjs-module.js';
    console.log(cjs.name);
    
  2. CommonJS模块不能直接导入ES模块,需要使用动态导入

    // cjs-app.js
    (async () => {
      const esm = await import('./esm-module.mjs');
      console.log(esm.name);
    })();
    

模块循环依赖

Node.js可以处理模块间的循环依赖,但需要注意导出值的状态:

// 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);

执行node main.js会输出:

main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done= true b.done= true

模块调试技巧

  1. 查看模块搜索路径

    console.log(module.paths);
    
  2. 获取当前模块信息

    console.log(module);
    
  3. 删除模块缓存(开发时热重载)

    delete require.cache[require.resolve('./module')];
    const freshModule = require('./module');
    
  4. 检查模块类型

    const fs = require('fs');
    console.log(require.resolve.paths('fs')); // null表示核心模块
    console.log(require.resolve.paths('lodash')); // 数组表示第三方模块
    

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌