您的位置:首页 > Web前端 > Node.js

node.js本地模块

2017-12-18 22:23 274 查看
nodejs有三种模块:内建模块(c++)、本地模块、用户模块

内建模块:使用c++编写,源码文件夹下src目录下很多node_开头的文件就是内建模块

本地模块:使用js编写,源码文件夹下lib目录下的都是本地模块,以上两种模块都会被编译到node二进制命令文件中去

用户模块:node_modules目录下的就是,分为全局安装和非全局的

本地模块的定义:

./lib/bootstrap_node.js
const ContextifyScript = process.binding('contextify').ContextifyScript;

//根据字符串代码,获取其中定义的匿名函数
function runInThisContext(code, options) {
const script = new ContextifyScript(code, options);
return script.runInThisContext();
}

//本地模块类,与普通模块相比,没有子模块,因为在其中不调用其他模块
//本地模块指的是源代码目录下lib子目录下的js模块
function NativeModule(id) {
this.filename = `${id}.js`;
this.id = id;
//导出对象
this.exports = {};
//是否已加载
this.loaded = false;
//是否正在加载
this.loading = false;
}

//本地模块脚本存储,c++中加载脚本,作为字符串存储
NativeModule._source = process.binding('natives');
//模块实例缓存
NativeModule._cache = {};

//内建配置对象
const config = process.binding('config');

//require函数,用来获取本地模块,也可以获取NativeModule类
//注意require获取'native_module'时并不是获取对应的js文件
NativeModule.require = function(id) {
if (id === 'native_module') {
return NativeModule;
}

//先从缓存中获取
const cached = NativeModule.getCached(id);
//如果获取到,且已加载或正在加载,直接返回导出对象
if (cached && (cached.loaded || cached.loading)) {
return cached.exports;
}

//不包含指定模块,抛出错误
if(!NativeModule.exists(id)) {
// Model the error off the internal/errors.js model, but
// do not use that module given that it could actually be
// the one causing the error if there's a bug in Node.js
const err = new Error(`No such built-in module: ${id}`);
err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';
err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';
throw err;
}

//模块加载列表加入代表本地模块的字符串
process.moduleLoadList.push(`NativeModule ${id}`);

//构造本地模块
const nativeModule = new NativeModule(id);

//将本地模块实例缓存到构造函数上
nativeModule.cache();
//编译本地模块,执行脚本获取导出对象
nativeModule.compile();

//返回导出对象
return nativeModule.expo
4000
rts;
};
//./lib/dep文件夹下的js模块使用这个函数来获取
NativeModule.requireForDeps = function(id) {
if (!NativeModule.exists(id) ||
// TODO(TimothyGu): remove when DEP0084 reaches end of life.
id.startsWith('node-inspect/') ||
id.startsWith('v8/')) {
id = `internal/deps/${id}`;
}
return NativeModule.require(id);
};

//获取指定名称缓存模块
NativeModule.getCached = function(id) {
return NativeModule._cache[id];
};

//指定名称本地模块是否存在
NativeModule.exists = function(id) {
return NativeModule._source.hasOwnProperty(id);
};

//如果配置指明暴露internal文件夹下模块
if (config.exposeInternals) {

//不区分是否是内部模块
NativeModule.nonInternalExists = NativeModule.exists;

//则没有模块是内部模块
NativeModule.isInternal = function(id) {
return false;
};
}
//如果不暴露内部模块
else {
//当模块存在且不是内部模块时返回true
NativeModule.nonInternalExists = function(id) {
return NativeModule.exists(id) && !NativeModule.isInternal(id);
};

//模块id以internal开头为内部模块
NativeModule.isInternal = function(id) {
return id.startsWith('internal/');
};
}

//根据id获取本地模块脚本字符串
NativeModule.getSource = function(id) {
return NativeModule._source[id];
};

//包裹本地模块脚本
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};

//用于包裹的字符串,最后会被包裹为一个括号括住的匿名函数表达式
NativeModule.wrapper = [
'(function (exports, require, module, internalBinding, process) {',
'\n});'
];

//编译模块,相当于根据模块名初始化,执行获取导出对象
NativeModule.prototype.compile = function() {
//获取脚本
var source = NativeModule.getSource(this.id);
//包裹脚本
source = NativeModule.wrap(source);

//正在加载
this.loading = true;

try {
//获取本地模块被包裹之后的匿名函数
const fn = runInThisContext(source, {
filename: this.filename,
lineOffset: 0,
displayErrors: true
});
//获取对应的require函数
const requireFn = this.id.startsWith('internal/deps/') ?
NativeModule.requireForDeps :
NativeModule.require;
//执行函数,传入当前模块实例的属性
fn(this.exports, requireFn, this, internalBinding, process);

//已加载
this.loaded = true;
} finally {
this.loading = false;
}
};

//在构造函数上缓存本地模块
NativeModule.prototype.cache = function() {
NativeModule._cache[this.id] = this;
};

1.本地模块一开始只缓存了字符串脚本

2.有一个特殊的native_module并没有对应的js文件

3.internal文件夹下模块是否可用需要根据配置决定

4.模块脚本要被包裹为一个匿名函数定义来执行

5.包裹函数参数有exports、require、module、process等等,这里解释了在模块文件中exports与module.exports不能随意混用的原因,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: