一文了解Node中的文件模块和核心模块

javascriptjavascript 2023-08-29 06:22:23 881
摘要: 本篇文章带大家了解一下Node中的文件模块和核心模块,聊聊文件模块的查找和文件模块的编译执行、JavaScript与C/C++核心模块的编译执行,希望对大家有所帮助!在我们使用Nodejs进行日常开发时,经常会使用require导入两...

本篇文章带大家了解一下Node中的文件模块和核心模块,聊聊文件模块的查找和文件模块的编译执行、JavaScript与C/C++ 核心模块的编译执行,希望对大家有所帮助!

在我们使用 Nodejs 进行日常开发时,经常会使用 require 导入两类模块,一类是我们自己编写的模块或使用 npm 安装的第三方模块,这类模块在 Node 中称为 文件模块;另一类则是 Node 内置的提供给我们使用的模块,如 osfs 等模块,这些模块被称为 核心模块

需要注意的是,文件模块与核心模块的差异不仅仅在于是否被 Node 内置,具体到模块的文件定位、编译和执行过程,两者之间都存在明显的差别。不仅如此,文件模块还可以被细分为普通文件模块、自定义模块或 C/C++ 扩展模块等等,不同的模块在文件定位、编译等流程也存在诸多细节上的不同。

我们先从文件模块讲起。

文件模块

什么是文件模块呢?

在 Node 中,使用 .、.. 或 / 开头的模块标识符(也就是使用相对路径或绝对路径)来 require 的模块,都会被当作文件模块。另外,还有一类特殊的模块,虽然不含有相对路径或绝对路径,也不是核心模块,但是会指向一个包,Node 在定位这类模块时,会用 模块路径 逐个查找该模块,这类模块被称为自定义模块。

因此,文件模块包含两类,一类是带路径的普通文件模块,一类是不带路径的自定义模块。

文件模块在运行时动态加载,需要完整的文件定位、编译执行过程,速度比核心模块慢。

对于文件定位而言,Node 对这两类文件模块的处理有所不同。我们来具体看看这两类文件模块的查找流程。

普通文件模块的查找

对于普通的文件模块,由于携带路径,指向非常明确,查找耗时不会很久,因此查找效率比下文要介绍的自定义模块要高一些。不过还是有两点需要注意。

一是通常情况下,使用 require 引入文件模块时一般都不会指定文件扩展名,比如:

const math = require("math");

由于没有指定扩展名,Node 还不能确定最终的文件。在这种情况下,Node 会按 .js、.json、.node 的顺序补足扩展名,依次尝试,这个过程被称为 文件扩展名分析

另外需要注意的是,在实际开发中,除了 require 一个具体的文件外,我们通常还会指定一个目录,比如:

const axios = require("../network");

在这种情况下,Node 会先进行文件扩展名分析,如果没有查找到对应文件,但是得到了一个目录,此时 Node 会将该目录当作一个包来处理。

具体而言,Node 会将目录中的 package.jsonmain 字段所指向的文件作为查找结果返回。如果 main 所指向的文件错误,或者压根不存在 package.json 文件,Node 会使用 index 作为默认文件名,然后依次使用 .js.node 进行扩展名分析,逐个查找目标文件,如果没有找到的话就会抛出错误。

(当然,由于 Node 存在两类模块系统 CJS 和 ESM,除了查找 main 字段外,Node 还会采用其他方式,由于不在本文讨论范围内,就不再赘述了。)

自定义模块的查找

刚才提到,Node 在查找自定义模块的过程中,会使用到模块路径,那什么是模块路径呢?

熟悉模块解析的朋友应该都知道,模块路径是一个由路径组成的数组,具体的值可以看以下这个示例:

// example.js
console.log(module.paths);

打印结果:

1.png

可以看到,Node 中的模块存在一个模块路径数组,存放在 module.paths 中,用于规定 Node 如何查找当前模块引用的自定义模块。

具体来讲,Node 会遍历模块路径数组,逐个尝试其中的路径,查找该路径对应的 node_modules 目录中是否有指定的自定义模块,如果没有就向上逐级递归,一直到根目录下的 node_modules 目录,直到找到目标模块为止,如果找不到的话就会抛出错误。

可以看出,逐级向上递归查找 node_modules 目录是 Node 查找自定义模块的策略,而模块路径便是这个策略的具体实现。

同时我们也得出一个结论,在查找自定义模块时,层级越深,相应的查找耗时就会越多。因此相比于核心模块和普通的文件模块,自定义模块的加载速度是最慢的。

当然,根据模块路径查找到的仅仅是一个目录,并不是一个具体的文件,在查找到目录后,同样地,Node 会根据上文所描述的包处理流程进行查找,具体过程不再赘述了。