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。返回目录