/ 前端

modules in Node.js

模块机制是node js中很重要的一个基本概念。
在node js的模块系统中,每个文件都是一个独立的模块。
比如说在文件circle.js中:

// circle.js
const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;

其中exports是一个全局的对象,任何附加到exports上的属性都将被添加到该模块的根路径下。
在该模块中定义的变量被wrap在模块内部,比如这里定义的PI

require.resolve()

当require一个文件时,发生了什么呢?

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with '/'
   a. set Y to be the filesystem root
3. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found"

虽然有些繁琐,不过这对理解node js的文件系统十分必要。然后我们来看一下每一步resolve()都做了哪些工作。首先是文件的解析:

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.json is a file, parse X.json to a JavaScript Object.  STOP
4. If X.node is a file, load X.node as binary addon.  STOP

Node.js 支持多种文件类型,其中*.node为C/C++addon编译文件。
然后是目录文件的解析:

LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
   d. LOAD_INDEX(M)
2. LOAD_INDEX(X)

所以package.json文件是node解析时的指路明灯,会先在目录下找这个文件,如果有的话,按照文件配置来resolve主文件,如果没有的话,去尝试解析index文件,包含js, json, 以及node文件类型。
如此,可以将模块分别放到独立的文件夹中,只要在package.json中指明主文件和版本等信息即可。
最后是node_modules部分的解析:

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

首先找出所有层级的node_modules文件路径,然后在遍历每个node_modules路径,从该文件中尝试解析文件和目录。
注意,NODE_MODULES_PATHS中解析出的node_modules顺序是由内往外的。

Caching

正常情况下,文件只会在require的时候执行一次,然后将会缓存起来,以后的require将不会再执行(除非require.resolve()出不同的文件路径)
所以要想多次执行,只能将功能定义到export出的object中。
当然,你也可以手动清除缓存:
delete require.cache[pkg_name]
但是请注意,在手动清除缓存时,请确保该module对应的所有引用都已经被合理清楚,否则,很容易造成内存泄漏。

modules in Node.js
Share this