Node.js require(cjs)
And Cyclic dependency
// 617行
Module.prototype.require = function(id) {
if (typeof id !== 'string') {
throw new ERR_INVALID_ARG_TYPE('id', 'string', id);
}
if (id === '') {
throw new ERR_INVALID_ARG_VALUE('id', id,
'must be a non-empty string');
}
return Module._load(id, this, /* isMain */ false);
};
// 479行
// module load
Module._load = function(request, parent, isMain) {
if (parent) {
debug('Module._load REQUEST %s parent: %s', request, parent.id);
}
// 判断是否是 ECMAScript Modules 以及 入口文件
// 调用esm的加载方法
if (experimentalModules && isMain) {
asyncESM.loaderPromise.then((loader) => {
return loader.import(getURLFromFilePath(request).pathname);
})
.catch((e) => {
decorateErrorStack(e);
console.error(e);
process.exit(1);
});
return;
}
// 获取要加载的文件名
var filename = Module._resolveFilename(request, parent, isMain);
// 判断缓存
var cachedModule = Module._cache[filename];
if (cachedModule) {
// 当前模块挂载到父模块上 并且返回已经缓存好的模块exports
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
// 判断是否是native模块
if (NativeModule.nonInternalExists(filename)) {
debug('load native module %s', request);
return NativeModule.require(filename);
}
// Don't call updateChildren(), Module constructor already does.
// 初始化一个Module
var module = new Module(filename, parent);
// 主模块挂在到process的mainModule上
if (isMain) {
process.mainModule = module;
module.id = '.';
}
// 添加到缓存中
Module._cache[filename] = module;
// 加载模块
tryModuleLoad(module, filename);
// 返回模块的exports
return module.exports;
};
// 536行
// 获取module的文件名
Module._resolveFilename = function(request, parent, isMain, options) {
// native模块不做处理
if (NativeModule.nonInternalExists(request)) {
return request;
}
var paths;
// 获取所有可能的路径
paths = Module._resolveLookupPaths(request, parent, true);
// look up the filename first, since that's the cache key.
// 获取最终模块对应的文件名
var filename = Module._findPath(request, paths, isMain);
if (!filename) {
// eslint-disable-next-line no-restricted-syntax
var err = new Error(`Cannot find module '${request}'`);
err.code = 'MODULE_NOT_FOUND';
throw err;
}
return filename;
};
// _resolveLookupPaths 和 _findPath 不重点分析
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
updateChildren(parent, this, false);
this.filename = null;
this.loaded = false;
this.children = [];
}
// 将新的children挂载到parent上
function updateChildren(parent, child, scan) {
var children = parent && parent.children;
if (children && !(scan && children.includes(child)))
children.push(child);
}
// 同一个模块会被 Module._cache 和 父模块引用
// 热更新如果只是简单的清除掉_cache中的模块,会导致每次新建一个module,
// 构造函数将新模块不断放入父模块的children中,导致内存泄漏
function tryModuleLoad(module, filename) {
var threw = true;
try {
module.load(filename);
threw = false;
} finally {
if (threw) {
delete Module._cache[filename];
}
}
}
Module.prototype.load = function(filename) {
debug('load %j for module %j', filename, this.id);
assert(!this.loaded);
this.filename = filename;
// 获取这个module路径上所有可能的node_modules路径
this.paths = Module._nodeModulePaths(path.dirname(filename));
// 处理文件扩展名
var extension = path.extname(filename) || '.js';
if (!Module._extensions[extension]) extension = '.js';
// 使用_extensions方法加载不同格式的文件
Module._extensions[extension](this, filename);
// 模块加载的标志位
this.loaded = true;
// esm模块的处理
if (experimentalModules) {
}
};
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
};
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
content = stripShebang(content);
// create wrapper function
// 用 function (exports, require, module, __filename, __dirname) {} 来包装模块代码
// 五个参数:exports 当前module的exports require 在Module.prototype.require上改造的方法
// module: 当前module __filename 当前文件绝对路径 __dirname 当前文件所在文件夹的绝对路径
var wrapper = Module.wrap(content);
// 用V8暴露的方法来构造为一个新函数
var compiledWrapper = vm.runInThisContext(wrapper, {
filename: filename,
lineOffset: 0,
displayErrors: true
});
// 根据当前环境来执行构造出来的新函数
var inspectorWrapper = null;
if (process._breakFirstLine && process._eval == null) {
if (!resolvedArgv) {
// we enter the repl if we're not given a filename argument.
if (process.argv[1]) {
resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
} else {
resolvedArgv = 'repl';
}
}
// Set breakpoint on module start
if (filename === resolvedArgv) {
delete process._breakFirstLine;
inspectorWrapper = process.binding('inspector').callAndPauseOnStart;
}
}
var dirname = path.dirname(filename);
var require = makeRequireFunction(this);
var depth = requireDepth;
if (depth === 0) stat.cache = new Map();
var result;
if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, this.exports, this.exports,
require, this, filename, dirname);
} else {
result = compiledWrapper.call(this.exports, this.exports, require, this,
filename, dirname);
}
if (depth === 0) stat.cache = null;
return result;
};
// 其他格式的处理
// Native extension for .json
Module._extensions['.json'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
};
//Native extension for .node
Module._extensions['.node'] = function(module, filename) {
return process.dlopen(module, path.toNamespacedPath(filename));
};
if (experimentalModules) {
Module._extensions['.mjs'] = function(module, filename) {
throw new ERR_REQUIRE_ESM(filename);
};
}
循环引用
a.js
console.log('a 开始');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log('a 结束');
b.js
console.log('b 开始');
exports.done = false;
const a = require('./a.js');
console.log('在 b 中,a.done = %j', a.done);
exports.done = true;
console.log('b 结束');
main.js
console.log('main 开始');
const a = require('./a.js');
const b = require('./b.js');
console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);
Nodejs
By Joson Chen
Nodejs
- 1,062