Node.js模块加载的详细方案
Javascript是世界上使用最频繁的编程语言之一。它是万维网的通用语言,被所有浏览器所使用,Javascript的诞生可以追溯到网景时代。它的核心内容是在当时迅速发展起来与微软作战,并参与激烈的浏览器大战,由于早期的发布,不可避免地造成了它的一些不良特性。虽然它的开发时间很短,但Javascript仍然有许多强大的特性。但是,每个脚本共享一个全局名称空间。
一旦Web页面加载了Javascript代码,它将被注入到全局命名空间中,所有其他加载的脚本都共享一个地址空间,这将导致许多安全问题、冲突和一些常见问题,从而使bug难以跟踪,难以解决。
但谢天谢地,节点已经制定了一些服务器端Javascript标准,并实施了CommonJS模块的标准。在这个标准中,每个模块都有自己的上下文,并将其与其他模块区别开来,这意味着这些模块不会污染全局范围,因为没有所谓的全局范围,模块不会互相干扰。
在本章中,我们将了解几个不同的模块以及如何加载它们。
将代码拆分成一系列定义良好的模块可以帮助您控制应用程序。接下来,我们将学习如何创建和使用自己的模块。
了解节点如何加载模块
在节点中,模块可以由文件路径引用,也可以由模块名引用。如果Node被该名称引用,则模块名称将被引用到相应的模块文件路径中。
非核心模块包括第三方模块安装NPM(节点的软件包管理器),以及当地的模块,你或你的同事创造。
当前脚本导入的每个模块向程序员公开一组公共API。在使用模块之前,您需要将其导入需求函数,如下所示。
复制代码代码如下所示:
VaR模块=需要('module_name)
上面的代码将导入一个模块被称为module_name。它可能是一个核心模块,或者模块安装与NPM,并要求函数返回包含所有模块的公共API的一个对象。根据模块返回的对象可以是任何Javascript值。它可以是包含一系列属性(函数、数组或任何Javascript对象)的函数或对象。
输出模块
CommonJS模块系统之间共享文件的对象和函数在节点的唯一途径。一个非常复杂的程序,你应该形成一系列的一些类,定义良好的模块可重用的对象,或功能模块用户模块只能暴露你的代码指定。
在下面的示例中,您将了解到文件和模块在节点中是一一对应的。我们创建了一个名为circle.js,仅出口Circle构造器。
复制代码代码如下所示:
函数圆(x,y,r){
功能r_squared(){
返回Math.pow(R,2);
}
函数区域(){
返回math.pi * r_squared();
}
返回{区域:区域};
}
module.exports =圈;
在代码中最重要的是最后一行,它定义了模块的出口。模块是一个特殊的变量,它表示当前模块本身,而module.exports是来自外国的目标模块,它可以是任何对象,在这种情况下,我们得到了圆的构造函数,该模块用户可以使用该模块创建圆的实例。
还可以导出一些复杂的对象。module.exports初始化为空的对象,和你出口的任何内容,你想向外界揭露作为module.exports对象的属性。例如,你设计了一个模块,提供一组函数外面的世界:
复制代码代码如下所示:
功能打印(){
console.log(A);
}
功能printb(){
console.log(B);
}
功能printc(){
console.log(C);
}
module.exports.printa =打印;
module.exports.printb = printb;
module.exports.pi = math.pi;
这两个功能模块出口(打印和printb)和数量(PI),并调用代码看起来像这样。
复制代码代码如下所示:
无功mymodule2 =需要('。 / mymodule2);
mymodule2.printa(一); / / ->
mymodule2.printb(B); / / ->
console.log(mymodule2。PI); / /:3.141592653589793
加载模块
正如前面提到的,你可以使用需要函数加载模块,而且不用担心打电话要求代码会影响全局命名空间,因为没有节点的全局命名空间的概念。如果模块存在,没有语法或初始化错误,require函数将返回到模块的对象,你可以指定对象的任何局部变量。
有几种不同类型的模块。他们大致可以分为核心模块,本地模块和第三方模块安装到新公共管理。根据模块的类型,有几种引用模块的方法。接下来我们将了解这些知识。
加载的核心模块
节点有一些被编译成二进制文件的模块,称为核心模块,不能通过路径引用,只使用模块名。核心模块具有最高的负载优先级,即使您拥有同名的第三方模块,核心模块也将首先加载。
例如,如果您想加载和使用http核心模块,您可以这样做:
复制代码代码如下所示:
var http =需要('http');
这将返回一个API,包含HTTP模块对象,它包含在节点的API文档中定义的HTTP模块。
加载文件模块
还可以使用绝对路径从文件系统加载模块:
复制代码代码如下所示:
VaR的mymodule =需要( / / / my_modules佩德罗回家/ my_module);
您还可以使用基于当前文件的相对路径:
复制代码代码如下所示:
VaR的mymodule =需要(.. / my_modules / my_module);
无功mymodule2 =需要('。 / / my_module_2库);
在上面的代码中的通知,你可以忽略文件扩展名,如果节点找不到文件,文件名会再次找到与后缀JS(译者注:实际上,除了JS,寻找JSON和节点,可以看到具体的网站文件),因此,如果在当前目录中的一个叫my_module.js,将有以下两种加载方式:
复制代码代码如下所示:
VaR的mymodule =需要('。 / my_module);
VaR的mymodule =需要('。 / my_module .js);
加载目录模块
您还可以使用目录路径加载模块:
复制代码代码如下所示:
VaR的mymodule =需要('。 / mymoduledir);
节点假定目录是一个模块的封装和试图搜索包定义文件,package.json,这个目录下。
如果没有找到,节点将认为包的入口点(除index.js文件index.js会发现指数。节点。节点的文件是一个二进制节点扩展包,看到正式文件),上面的代码,例如,节点会尝试找一个mymoduledir / / index.js文件。
相反,如果你找到package.json文件,节点将尝试解析它,并在包定义中找到的主要属性,然后采取的主要属性值作为切入点的相对路径。在这种情况下,如果package.json定义如下:
复制代码代码如下所示:
{
名字:mymodule
主:, / / mymodule JS库。
}
节点会尝试加载。 / / / mymodule.js mymoduledir lib文件
从node_modules目录负荷
如果需要功能不是相对路径或核心模块名称参数,节点会寻找它的node_modules当前目录下的子目录,如下面的代码中,节点将试图找到文件。 / / node_modules mymodule。JS:。
复制代码代码如下所示:
VaR的mymodule =需要('mymodule .js);
如果没有找到,节点将继续在上级目录的node_modules文件夹搜索。如果尚未找到,它将继续搜索上层目录,直到找到相应的模块或到达根目录。
或者你可以用这个模块来管理node_modules目录,但最好是模块管理任务NPM(见1章),当地node_modules NPM目录是安装模块的默认位置,该节点和NPM的设计联系在一起的。通常,开发商不关心这个功能,你可以简单地安装、更新和使用NPM删除包,这将帮助你保持node_modules目录。
缓存模块
在第一次成功加载之后,模块将被缓存。也就是说,如果模块名称解析到同一个文件的路径,每一次,打电话要求(mymodule)将完全回到相同的模块。
例如,有一个叫my_module.js模块,主要包括以下几个方面:
复制代码代码如下所示:
console.log('module my_module初始化…;
module.exports =函数(){
console.log(嗨!);
};
console.log('my_module初始化。);
然后模块加载以下代码:
复制代码代码如下所示:
无功mymoduleinstance1 =需要('。 / my_module);
它产生以下输出:
复制代码代码如下所示:
my_module初始化模块…
my_module初始化
如果我们进口两次:
复制代码代码如下所示:
无功mymoduleinstance1 =需要('。 / my_module);
无功mymoduleinstance2 =需要('。 / my_module);
输出仍然:
复制代码代码如下所示:
my_module初始化模块…
my_module初始化
也就是说,模块的初始化代码只执行一次。当你建立你自己的模块时,如果模块的初始化代码包含有副作用的代码,你必须特别注意这个特性。
总结
节点取消Javascript默认的全球范围,而不是使用CommonJS模块系统,这样你就可以更好地组织你的代码,从而避免了许多安全问题和错误,你可以使用require函数加载的核心模块,第三方模块,或加载自己的模块文件和目录
还可以使用相对路径或绝对路径加载非核心模块。如果你把模块在node_modules目录,或安装模块的NPM,你可以使用模块名直接加载。
译者注:
建议读者阅读官方文件的模块章节。个人感觉比作者更清晰,并添加了一个很有代表性的例子。这将有助于理解节点模块的加载。
复制代码代码如下所示:
在需要(x)加载路径y下的模块
1。如果x是核心模块,
A.加载并返回到核心模块
b.端
2。如果X或.. / /'开始'。
A. load_as_file(Y + X)
B. load_as_directory(Y + X)
三.load_node_modules(x,dirname(Y))
4。除了异常:未找到
load_as_file(X)
1。如果x是一个文件,x作为Javascript脚本加载,加载结束。
2。如果X.js是一个文件,X.js作为一个Javascript脚本加载和加载的最后完成
三.如果X.node是一个文件,加载X.node作为节点的二进制插件和结束后装
load_as_directory(X)
1。如果X / package.json文件存在,
A.解析X / package.json找主场。
m=x(主字段的值)
C. load_as_file(M)
2。如果X / index.js文件存在,X / index.js为Javascript脚本加载和加载的最后完成
三.如果X / index.node文件存在,负荷X / index.node作为节点的二进制插件,和结束后加载完成时
load_node_modules(x,开始)
1。另一个= node_modules_paths(开始)
2。以下操作的每一个目录的目录下的目录:
A. load_as_file(目录/ X)
B. load_as_directory(目录/ X)
node_modules_paths(开始)
1。另一部分=路径拆分(开始)
2。另一根=指数第一审node_modules 的部分,或0
三.另一个=零件数- 1
其他4。目录= { }
5。当我,
如果部分{我} =node_modules 继续后续操作,否则下一环
路径连接(零件{ 0)我} +node_modules )
B.目录=目录+目录
C.也I = i - 1
6。返回目录