Require加载module机制详解
加载文件类型
node中的文件和模块是一一对应的。一个node.js文件就是一个模块,但是这个文件可能是js代码,JSON或者编译过的C/C++拓展
加载流程图
加载顺序和流程如下图所示,已经加载过的文件会直接缓存起来,下次再require会直接从缓存加载。
加载文件详细过程
- 如果 X 是内置模块
a. 返回内置模块
b. 停止执行 - 如果 X 以 ‘/’ 开头
a. 设置 Y 为文件根路径 - 如果 X 以 ‘./’ 或 ‘/’ or ‘…/’ 开头
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X) - LOAD_NODE_MODULES(X, dirname(Y))
- 抛出异常 “not found”
LOAD_AS_FILE(X)
- 如果 X 是一个文件, 将 X 作为 JavaScript 文本载入并停止执行。
- 如果 X.js 是一个文件, 将 X.js 作为 JavaScript 文本载入并停止执行。
- 如果 X.json 是一个文件, 解析 X.json 为 JavaScript 对象并停止执行。
- 如果 X.node 是一个文件, 将 X.node 作为二进制插件载入并停止执行。
LOAD_AS_DIRECTORY(X)
- 如果 X/package.json 是一个文件,
a. 解析 X/package.json, 并查找 “main” 字段。
b. let M = X + (json main 字段)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M) - LOAD_INDEX(X)
LOAD_INDEX(X)
- 如果 X/index.js 是一个文件, 将 X/index.js 作为 JavaScript 文本载入并停止执行。
- 如果 X/index.json 是一个文件, 解析 X/index.json 为 JavaScript 对象并停止执行。
- 如果 X/index.node 是一个文件, 将 X/index.node 作为二进制插件载入并停止执行。
加载module详细过程
- 查找当前目录下面的node_modules目录
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X) - 如果没找到,再查询父目录的node_modules目录,直到找到或者到了根目录的node_modules目录(待查询目录在module对象里面的paths数组中)
示例
const demo1 = require('./demoModule/demo');
// ./demoModule/demo => console.log('demo file whitout .js')
// ./demoModule/demo.js => console.log('demo file whit .js')
const demo2 = require('./demoModule/demoWithJson');
// {
// "memo": "this is json file"
// }
console.log(demo2.memo)
const demo3 = require('./demoModule/demoWithJS');
// ./demoModule/demoWithJS => console.log('demo file whit .js')
// ./demoModule/demoWithJS.json =>
// {
// "memo": "this is demoWithJS.json"
// }
const demo4 = require('./demoModule');
// ./demoModule/index.js => console.log('this is index.js in demoModule')
const demo5 = require('./demoModule/dirWithPackage');
// ./demoModule/dirWithPackage/main.js => console.log('this is main.js in dirWithPackage')
// ./demoModule/dirWithPackage/package.json 里面的main字段为“main.js”
// const demo6 = require('./demoModule/demoNode');
const demo7 = require('demo')
/*
./node_modules/demo/index.js
// 源码:
console.log('this is index.js in node_modules/demo')
const subDemo = require('subdemo');
const subDemo2 = require('subdemo2');
// subdemo 和 subdemo2所在位置和源码
./node_modules/demo/node_modules/subdemo/index.js
=> console.log('this is index.js in ./node_modules/demo/node_modules/subdemo')
./node_modules/subdemo2/index.js
=> console.log('this is index.js in ./node_modules/subdemo2')
*/
const demo8 = require('demo2')
// ./node_modules/demo2/index.js => console.log('this is index.js in ./node_modules/demo2')
// ./node_modules/demo2.js => console.log('this is demo2.js in ./node_modules')
const subDemo3 = require('subdemo3');
// ../node_modules/subdemo3/index.js
const subDemo4 = require('subdemo4');
// ../../node_modules/subdemo4/index.js
const subDemo5 = require('subdemo5');
// /Users/username/.node_modules/subdemo5.js
输出结果
demo file whitout .js
this is json file
demo file whit .js
this is index.js in demoModule
this is main.js in dirWithPackage
this is index.js in node_modules/demo
this is index.js in node_modules/demo/node_modules/subdemo
this is index.js in node_modules/subdemo2
this is demo2.js in ./node_modules
this is index.js in …/node_modules/subdemo3
this is index.js in …/…/node_modules/subdemo4
this is subdemo5.js in /Users/lifugui/.node_modules
其实,module.paths 里面已经包含了上述这些会去查找的目录以及优先级
// 当前文件目录 /Users/username/Documents/projects/nodeDemo
[
'/Users/username/Documents/projects/nodeDemo/repl/node_modules', // 这个目录是干嘛的?不清楚,实测并不会从这个目录加载module
'/Users/username/Documents/projects/nodeDemo/node_modules',
'/Users/username/Documents/projects/node_modules',
'/Users/username/Documents/node_modules',
'/Users/username/node_modules',
'/Users/node_modules',
'/node_modules',
'/Users/username/.node_modules', // 全局module 可以考虑放这两个目录
'/Users/username/.node_libraries'
]