当前位置: 首页 > news >正文

NodeJS里经常用到require,require的模块加载机制是什么

一、require模块是否需要import

用 Node.js 的 require 加载模块时,不需要用 import

这两者其实是两套不同的模块系统:

  • require 是 Node.js 传统的 CommonJS 模块加载方式。

  • import 是 ECMAScript 2015+(ES6)的 ES Modules 语法。


区别简要说明:

方式语法形式运行环境支持情况
requireconst x = require('x')Node.js 默认支持,所有版本CommonJS 模块,动态加载
importimport x from 'x'浏览器和 Node.js(需配置)ES Modules,静态加载


在 Node.js 中:

  • 如果用 .js 文件默认(CommonJS 模式),用 require 加载,不能用 import(会报错,除非用实验特性或 .mjs 文件)。

  • 如果想用 ES Module,文件一般用 .mjs 后缀或者 package.json 中设置 "type": "module",这时用 import,不能用 require


结论:

  • 你只用 require 就够了,不需要 import

  • 这两者是两种不同的模块加载语法,不要混用。


二、Node.js 中 require 的完整模块加载过程(重点版)

  1. 检查缓存区(require.cache

    • 先查用户加载的文件模块的缓存区(以文件绝对路径为key)。如果缓存命中,直接返回缓存内容。

    • 注意:缓存只针对文件模块(JavaScript 文件、JSON 文件、.node 二进制模块)。

  2. 判断是否是核心模块(Core Module)

    • 如果缓存没命中,接下来判断这个模块名是否是 Node.js 的核心模块(比如 fshttppath 等)。

    • 核心模块的加载是从 Node.js 内部直接提供的,不经过文件系统查找。

    • Node 有一个核心模块缓存(和 require.cache 不同),如果命中则直接返回核心模块的导出。

    • 如果核心模块第一次加载,会加载并缓存核心模块实例。

  3. 模块路径解析

    • 如果不是核心模块,进行路径解析,查找文件系统上的具体文件位置(支持相对路径、绝对路径、node_modules 目录等)。

  4. 读取并编译模块

    • 读取文件内容后根据类型执行对应操作(JavaScript、JSON、native addon)。

  5. 缓存模块

    • 编译执行完成后,将模块缓存到 require.cache

  6. 返回模块导出


核心模块缓存和文件模块缓存是分开的:

  • require.cache 只缓存文件模块的导出。

  • 核心模块缓存由 Node.js 内部管理,用户不可见,但效果是类似的:只会加载一次。


如果想看源码,Node.js 的模块加载逻辑主要在 lib/internal/modules/cjs/loader.js 中,尤其是:

  • Module._load 实现了缓存判断和核心模块判断的逻辑。

  • NativeModule.get 用于加载核心模块。


总结:

require('module') 过程是:1. 查看文件模块缓存 require.cache
2. 若没命中,判断是否核心模块(内置模块)- 是则从内置模块缓存返回
3. 否则解析文件路径,加载文件模块
4. 编译执行,缓存
5. 返回模块导出

你之前代码的补充说明

const mod1 = require('./mod'); // 第一次加载,mod.js被执行,结果被缓存
const mod2 = require('./mod'); // 第二次加载,直接从缓存中拿,mod.js不会再执行

第1步就是判断缓存,这样避免重复加载,提升性能。


如果你想,我可以帮你写个伪代码模拟这整个流程,方便你理解。需要吗?

三、require的模块加载机制

关于 Node.js 里的 require,它是用来加载模块的,背后有一套比较复杂的模块加载机制。下面我详细解释一下 require 的模块加载机制。


Node.js require 模块加载机制详解

1. 作用

require 用来加载和使用模块,它可以加载:

  • 核心模块(如 fspath

  • 文件模块(本地文件,JS、JSON、Node 原生支持的其他文件)

  • 第三方模块node_modules 里的包)


2. 加载流程

当执行 require('some-module') 时,Node 会经历如下步骤来找到并加载模块:

2.1 判断模块类型
  • 核心模块(Core Modules)
    例如 fshttppath 等,如果是核心模块,直接返回核心模块导出的对象,不访问文件系统。

  • 文件路径
    如果参数是相对路径或绝对路径(./foo../bar/abs/path),则加载指定路径的文件。

  • 第三方模块
    如果不是核心模块也不是路径,Node 会从当前目录开始向上查找 node_modules 目录,直到根目录。


2.2 路径解析(文件模块 & 第三方模块)
  • 如果传入的是文件路径(相对或绝对),Node 会按照下面顺序查找文件:

    1. 直接看路径对应的文件是否存在,比如 ./foo.js

    2. 如果路径是文件夹,Node 会尝试加载文件夹下的 package.json"main" 字段指向的入口文件

    3. 如果没找到 package.json"main",尝试加载文件夹下的 index.jsindex.json 等默认文件

  • 对第三方模块,Node 会依次向上查找最近的 node_modules 文件夹里的对应模块文件夹。


2.3 模块缓存
  • 缓存机制:当一个模块第一次被加载后,会被缓存,后续 require 同一模块时直接返回缓存,避免重复执行,提高效率。

  • 缓存存储在 require.cache 对象里,键是模块的绝对路径。


2.4 模块包装和执行
  • Node 会给每个模块包装一层函数,形成闭包。模块代码被包裹成如下形式:

(function (exports, require, module, __filename, __dirname) {// 模块代码在这里执行
});
  • 这样模块内部就可以使用 exports 导出接口,require 加载其他模块,module 表示当前模块等。

  • 模块执行时,Node 会传入这些参数,让模块成为一个私有作用域。


2.5 模块导出机制
  • 模块通过 exportsmodule.exports 导出内容。

  • exportsmodule.exports 的引用,直接修改 exports 不改变导出对象,赋值新对象要用 module.exports = ...


3. 额外说明

  • .js 文件被视作 JavaScript 脚本执行。

  • .json 文件会被自动解析成对象。

  • .node 文件是编译后的二进制 addon,会被加载执行。


总结

  1. 判断模块类型(核心模块,文件模块,第三方模块)

  2. 解析模块路径(寻找文件或目录)

  3. 加载模块(读取代码,封装成函数)

  4. 执行模块代码(提供闭包参数)

  5. 缓存模块导出结果

  6. 返回模块导出


如果你需要,我可以帮你写一个示例,演示 require 的缓存或者自定义模块加载过程,或者给你推荐一些原理文档。你需要吗?

四、require 的缓存机制演示

我先给你演示一下 Node.js 中 require缓存机制自定义模块加载过程,然后给你推荐一些深入理解 Node.js 模块加载机制的官方文档和源码相关资料。


1. require 的缓存机制演示

Node.js 中,当你用 require('module') 加载模块时,模块只会被加载执行一次,后续再 require 相同模块,直接从缓存拿,不会重新执行。

// mod.js
console.log('mod.js 执行了');
module.exports = { count: 0 };// main.js
const mod1 = require('./mod');
mod1.count++;
console.log('第一次载入:', mod1.count);const mod2 = require('./mod');
console.log('第二次载入:', mod2.count);// 输出:
// mod.js 执行了
// 第一次载入: 1
// 第二次载入: 1

解释:

  • 第一次 require('./mod'),文件执行,导出对象缓存起来。

  • 第二次 require('./mod'),直接从缓存拿,没有再执行 mod.js 文件。

  • mod1mod2 是同一个对象,count 的状态被共享。


2. 自定义模块加载过程简单示例

你可以在 Node.js 中自定义如何加载模块,利用 Module._load 或者实现自定义的 require 函数(常用场景是修改模块行为或加载非 .js 文件)。

以下示例是简单拦截 require 加载过程打印模块名:

const Module = require('module');
const originalLoad = Module._load;Module._load = function(request, parent, isMain) {console.log(`加载模块: ${request}`);return originalLoad.apply(this, arguments);
};const fs = require('fs'); // 会打印:加载模块: fs
const path = require('path'); // 会打印:加载模块: path
const mymod = require('./mod'); // 会打印:加载模块: ./mod

这个示例中,改写了内部的 _load 方法,实现了加载日志。


3. 推荐学习资源和官方文档

  • Node.js 模块官方文档(英文)
    详尽解释 CommonJS 模块加载流程,缓存机制,加载路径解析等。

  • Node.js 源码中的 Module 模块
    Node.js 模块加载器核心源码,理解底层机制非常关键。

  • 《深入浅出Node.js》(朴灵)
    书中对模块机制有详细介绍,通俗易懂。

  • Module._load 源码注释文章(中文)
    分析了 _load 方法的实现原理。


如果你需要,我还可以帮你写一个更复杂的自定义 require 例子(比如加载 JSON、文本文件或者给模块加缓存时间限制之类的)。你想看吗?

相关文章:

  • DAY 50 超大力王爱学Python
  • 电磁场与电磁波篇---电荷电流
  • 【Markdown】基础用法汇总(标题、列表、链接、图片、加粗斜体、上下角标、引用块、代码块、公式)
  • 香橙派3B学习笔记11:systemd服务管理初步测试
  • Kubernetes (K8S) 系统学习规划
  • 电商数据采集的技术分享
  • day54python打卡
  • 在Qt中使用OpenGL显示大量点(点云)
  • 最新文章 支持一下!!
  • 如何使用deepseek编写测试计划
  • 装饰模式Decorator Pattern
  • 手动实现 memcpy 函数
  • 红花多组学挖掘OGT1-文献精读146
  • Linux中的连接符
  • Error: UDF library “libudf“ not available at
  • MotleyCrew ——抛弃dify、coze,手动搭建多agent工作流
  • 【第四十周】眼动追踪基础
  • TC3xx学习笔记-启动过程详解(二)
  • 毕业答辩PPT快速制作指南
  • 关于京东数据的采集和应用||API数据采集接口
  • 做网站体会/百度问答平台
  • 动态网站开发与全程实例pdf/近三天新闻50字左右
  • 专门做正品的网站手机版/淄博seo培训
  • 短视频获客系统/手机清理优化软件排名
  • 做同城网站赚钱吗/网店如何营销推广
  • 中国在数码网站注册域名好>/怎么免费建个人网站