> 文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921131911617-506503557.jpg)

其实在JavaScript的发展中,它主要是在浏览器前端中被应用广泛。因为在实际应用中, JavaScript的表现能力主要取决于宿主环境的API支持程度, 在最早期,只有对BOM, DOM的支持,随着HTML5的出现,在浏览器中出现了更多,更强大的API供JavaScript调用,但是这些都是发生在前端,后端JavaScript的规范却远远落后。Java有class文件,Phthon有import机制,PHP有include和require,但是JavaScript通过script标签引入代码的方式显得杂乱无章,为我们的后期维护增加了难度。对于JavaScript来说,还有四大主要缺点:

> * 1.没有模块系统。
>
> * 2.标准库比较少。
>
> * 3.没有标准,统一的接口。
>
> * 4.缺乏包管理系统。

Node.js实现了一套非常易用的模块系统,而Node的包管理系统NPM对包规范的完好支持使得Node应用在开发过程事半功倍。这一篇文章,主要针对Node的模块以及包的实现进行说明。

**Node的模块规范**

其实模块的定义非常简单,主要分为模块引用,模块定义和模块标识三个部分。

**1)模块引用**

Node.js中存在require()方法,这个方法接受模块标识,以此引入一个模块的API到当前的上下文中。

**2)模块定义**

既然我们可以用require()来引入模块,那自然也可以引出模块。Node.js提供了exports对象用于导出当前模块的方法和变量,并且exports是唯一导出的出口。在每个模块中,存在一个module对象,表示模块本身,exports其实就是module的一个属性。在Node.js中,一个文件其实就是一个模块,将我们需要导出的方法和属性绑定在exports对象上作为属性就可以将该方法或属性导出。

在另一个模块,可以通过require()引入模块,就可以使用导出的方法sum()。

**3)模块标识**

模块标识其实传递给require()方法的参数,模块标识必须是符合驼峰命名的字符串或者以./,../开头的路径,引入模块模块标识可以省略.js后缀。

模块的好处是将特定的方法和变量限定在特定的作用域中,使得开发者完全不必去考虑变量污染的问题。

**Node.js的模块实现**

在Node.js中,有三类模块,其中一类是Node.js提供的核心模块,就比如上一篇说过的fs文件模块,database数据库模块,还有一类是开发者自行编写的文件模块,就比如刚才示例的test.js模块,第三类就是自定义模块,这是一种特殊的文件模块,一般是一个文件或包的形式,比如引入mysql所需的jar包。

在Node.js中引入模块,需要经历三步:

**(1)路径分析**

对于文件模块来说,引入时模块标识指明了确切的文件位置,所以在路径分析中可以省略大量时间,加载速度仅次于核心模块。

自定义模块则是会从项目根目录逐个比较路径,直到找到目标模块为止。所以,自定义模块的路径越深,路径分析的耗时越多,所以自定义模块的加载速度是最慢的。

**(2)文件定位**

刚才其实说过了,模块标识可以不包含后缀名,所以Node.js在文件定位时会依次补充.js,.json,.node后缀名,然后去进行文件定位,因为Node.js是单线程,所以文件定位时会发生堵塞,所以如果引入的模块后缀是.json或者.node,可以在引入的时候加上后缀,可以提高查找速度。

**(3)编译执行**

定义到具体文件后,Node.js会创建一个模块对象,然后将模块引入并且编译。每一个编译成功的模块其文件路径都会作为索引缓存在缓存对象上,以提高二次引入模块的性能。

核心模块在Node.js源代码的编译过程中,直接被编译成二进制文件,然后被直接加载到内存中,所以核心模块引入时,文件定位和编译执行这两个步骤可以直接跳过,并且核心模块在路径分析中会被优先判断,所以核心模块的加载速度是最快的。

文件模块则是在执行时动态加载,所以路径分析,文件定位以及编译执行这三个步骤都不可省略,所以加载速度比核心模块慢。

Node.js对引入过的模块会进行缓存,以减少二次引入模块的性能开销二次加载模块一律采用缓存优先方式。核心模块的缓存检查优先于文件模块。

**包管理工具NPM**

刚才说到Node模块,但是虽然我们可以引用模块,但是模块与模块之间仍然是散列在各地的,相互之间并不能直接引用。而Node的包管理工具NPM则将模块相互联系起来。包其实是在模块的基础上进一步组织JavaScript代码。

其实NPM会有一个包描述文件package.json,一般位于包的根目录, NPM的所有行为都与包描述文件息息相关。前面几篇有讲过NPM作为默认包管理工具,会作为Node环境被一起安装。

**NPM常用功能**

NPM帮助Node完成了第三方模块的发布,安装和依赖。因为有NPM的存在,Node和第三方模块之间形成了很好的一个生态系统,而且逐渐越来越强大。接下来大致讲解下几个NPM常见命令

* npm --version 查看当前NPM的版本

* npm 查看帮助说明

* npm help 查看具体命令说明

* 执行命令会在浏览器中打开对应命令的说明文档

* npm install 安装依赖包,默认使用–-save参数,即默认添加到package.json中

* 执行该命令,NPM会在当前目录创建node_modules目录,然后再node_modules创建对应依赖包的目录,然后将依赖包解压到该目录。

* npm init 在此目录初始化生成package.json文件

* npm uninstall 卸载依赖包, 默认使用-–save参数,即从package.json中移除

* npm ls查看当前目录的依赖包

* npm root -g 查看全局安装地址

* npm list 查看依赖的当前版本

**NPM存在的问题**

在NPM平台,每个人都可以分享包,所以包质量没有办法保证,而且Node.js运行在服务端,所以需要考虑安全问题。所以一个优秀的模块需要符合几大模块:

1. 具备良好的测试

1. 具备良好的文档

1. 具备良好的测试覆盖率

1. 具备良好的代码规范

今天内容就到这里了,其实这篇文章并没有涉及到代码的编写,而是从模块的角度去理解Node.js,Node.js通过模块规范,弥补了JavaSCript没有结构性的不足,而NPM通过对包的统一管理,使得项目开发中的依赖问题得到有效解决。

下一篇将从异步编程的角度带大家继续了解Node.js,下一篇再见!
**欢迎关注我个人公众号:程序猿周先森**
![file](https://img2018.cnblogs.com/blog/830272/201909/830272-20190921131912181-1121394007.jpg)

Node.js之模块机制的更多相关文章

  1. 深入浅出Node.js (2) - 模块机制

    2.1 CommonJS规范 2.1.1 CommonJS的出发点 2.1.2 CommonJS的模块规范 2.2 Node的模块实现 2.2.1 优先从缓存加载 2.2.2 路径分析和文件定位 2. ...

  2. Node.js的模块载入方式与机制

    Node.js中模块可以通过文件路径或名字获取模块的引用.模块的引用会映射到一个js文件路径,除非它是一个Node内置模块.Node的内置模块公开了一些常用的API给开发者,并且它们在Node进程开始 ...

  3. Node.js 事件循环机制

    Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...

  4. 使用yarn代替npm作为node.js的模块管理器

    使用yarn代替npm作为node.js的模块管理器 转 https://www.jianshu.com/p/bfe96f89da0e     Fast, reliable, and secure d ...

  5. Node.js Web模块

    什么是Web服务器? Web服务器是处理由HTTP客户端发送的,如web浏览器的HTTP请求的软件应用程序,并返回响应于客户端网页. Web服务器通常伴随着图片,样式表和脚本的HTML文档. 大多数W ...

  6. Node.js工具模块

    在Node.js的模块库中提供实用的模块数量. 这些模块都是很常见的,并同时开发基于任何节点的应用程序频繁使用. S.N. 模块的名称和说明 1 OS Module提供基本的操作系统相关的实用功能 2 ...

  7. node.js基础模块http、网页分析工具cherrio实现爬虫

    node.js基础模块http.网页分析工具cherrio实现爬虫 一.前言      说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherri ...

  8. Node.js:模块

    概要:本篇博客主要介绍node.js的模块 1.创建模块 在node.js中创建一个模块非常简单,因为一个文件就是一个模块.我们只需要明白如何从其他文件中获取这个模块.Node.js提供了 expor ...

  9. node.js之模块

    node.js之模块 1.自定义模块的设置 加载自定义模块利用require: eg: require('./custom_module.js') 2.从模块外部访问模块内的成员 2.1使用expor ...

随机推荐

  1. netty源码解解析(4.0)-18 ChannelHandler: codec--编解码框架

    编解码框架和一些常用的实现位于io.netty.handler.codec包中. 编解码框架包含两部分:Byte流和特定类型数据之间的编解码,也叫序列化和反序列化.不类型数据之间的转换. 下图是编解码 ...

  2. Spring Boot 默认指标从哪来?

    了解有关 Spring Boot 默认指标及其来源的更多信息. 您是否注意到 Spring Boot 和 Micrometer 为您的应用生成的所有默认指标? 如果没有 - 您可以将 actuator ...

  3. HMM学习

    参看博客: 1.https://www.cnblogs.com/skyme/p/4651331.html 2.https://blog.csdn.net/continueoo/article/deta ...

  4. Linux--shell的awk--10

    一.awk介绍 全称:由Aho Weinberger Kernaighan三个人的首字母组合而成 1970年第一次出现在Unix机器上,后来在开源领域使用它 awk是一种单独的编程语言解释器 awk报 ...

  5. JS中的分支结构

    if语句 语法: if (expression1) { } else if (expression2) { } else { } 执行机制: 先对expression1做判定,如果为真,执行对应的代码 ...

  6. js循环(while循环,do while循环,for循环)相关知识点及练习

    08.循环 1.循环! 循环的作用: 简化代码,处理重复执行的代码 遍历数组.json对象.节点集合 2.while循环 语法: while(循环的条件){ 循环体 } 3.循环的五大要素 循环变量 ...

  7. Linux shell 获得字符串所在行数及位置

    shell 获得字符串所在行数及位置 01 获取字符串所在的行数 方式一:用grep -n [root@root]# cat test apple bit create delect exe flow ...

  8. Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island)

    Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island) 深度优先搜索的解题详细介绍,点击 给定一个包含了一些 0 和 1的非空二维数组 grid ...

  9. Leetcode之深度优先搜索(DFS)专题-515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)

    Leetcode之深度优先搜索(DFS)专题-515. 在每个树行中找最大值(Find Largest Value in Each Tree Row) 深度优先搜索的解题详细介绍,点击 您需要在二叉树 ...

  10. sql server创建序列sequence

    1.创建一个序列对象 CREATE SEQUENCE [schema_name . ] sequence_name START WITH <constant> INCREMENT BY & ...