开篇前,我们先来看张图, 看node与W3C组织、CommonJS组织、ECMAScript之间的关系。

Node借鉴来CommonJS的Modules规范实现了一套非常易用的模块系统,NPM对Packages规范
的完好支持使得Node应用在开发过程中事半功倍。

一、CommonJS 的模块规范

CommonJS中的大部分规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字、单元测试、Web服务器网关接口、包管理等。

1.1 模块引用
模块示例代码如下:

  1. var math = require('math');

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

1.2模块定义

在模块中,上下文提供require()方法引入外部模块。对应引入的功能,上下文提供了exports对象用于导出当前模块的方法或者变量,并且它是唯一导出的出口。

例如:

  1. // math.js
  2. exports.add = function () {
  3. var sum = ,
  4. i = ,
  5. args = arguments,
  6. l = args.length;
  7. while (i < l) {
  8. sum += args[i++];
  9. }
  10. return sum;
  11. };
  12.  
  13. // program.js
  14. var math = require('math');
  15. exports.increment = function (val) {
  16. return math.add(val, );
  17. };

1.3模块标识

简单理解就是传递给require()方法的参数,必须使用小驼峰命名的字符串,或者使用相对路径,亦可使用绝对路径。

Tip: 可以没有.js文件名后缀。

CommonJS导出和引入机制,我们不需要考虑变量污染、命名空间等。

二、Node的模块实现

在Node中引入模块,要经历3个步骤:
(1) 路径引入
(2) 文件定位
(3) 编译执行

Node模块分两种,一种是Node提供的模块,称为核心模块;另外一种是用户编写的模块,称为文件模块。

1、核心模块在Node源代码的编译过程中,编译进了二进制执行文件。在Node进程启动时,部分核心模块就被直接加载进内存中,所以这部分核心模块引入时,文件定位和编译执行这两个步骤可以省略掉,并且在路径分析中优先判断,所以它的加载速度是最快的。

2、文件模块在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程,速度比核心模块慢。

2.1优先从缓存加载

2.2路径分析和文件定位

Tip: 同步配合缓存,缓解Node单线程中阻塞式调用的缺陷

1)模块标识符分析

require()方法接受一个标识符作为参数,在Node实现中,正是基于这样一个标识符进行模块查找的。

2)自定义模块

它的生成方式与Javascript的原型链的查找方式十分类型。在加载过程中,Node会逐个尝试模块路径中的路径,知道找到目标文件为止。可以看出,当前文件的路径越深,模块查找耗时会越多,这是自定义模块加载速递最慢的原因。

2.3模块编译

每个文件模块都是一个对象,例如:

  1. function Module(id, parent) { this.id = id;
  2. this.exports = {}; this.parent = parent;
  3. if (parent && parent.children) {
  4. parent.children.push(this);
  5. }
  6. this.filename = null; this.loaded = false; this.children = [];
  7. }

编译和执行是引入文件模块的最后一个阶段。定位到具体的文件后,Node会新建一个模块对象,然后根据路径载入并编译。对于不同的文件拓展名,其载入方法也有所不同,例如:

1).js文件:通过fs模块同步读取文件后编译执行

2).node文件:这是用C/C++编写的拓展文件,通过dlopen()方法加载最后编译生成的文件

3).json文件:通过fs模块同步读取文件后,用JSON.parse()解析返回结果

4)其余拓展名文件:它们都被当作.js文件载入

Tip: 每一个编译成功的模块都会将文件路径作为索引缓存在Module._cache对象上,为了提高二次引入的性能。

根据不同的文件拓展名,Node会调用不同的读取方式,例如.json文件调用:

  1. // Native extension for .json
  2. Module._extensions['.json'] = function(module, filename) {
    var content = NativeModule.require('fs').readFileSync(filename, 'utf8'); try {
  3. module.exports = JSON.parse(stripBOM(content));
  4. } catch (err) {
  5. err.message = filename + ': ' + err.message; throw err;
  6. } };
  1. Module._extensions会被赋值给require()的extensions属性
  1. console.log(require.extensions);
    //{ '.js': [Function], '.json': [Function], '.node': [Function]

2.4 Javascript核心模块的编译过程

1)编译JavaScript核心模块

lib目录下的所有模块文件也没有定义require、module、export这些变量。在引入javascript核心模块的过程中,也经历了头尾包装的过程,然后才执行和导出exports对象。与文件模块有区别的地方在于:获取源代码的方式(核心模块是从内存中加载的)以及缓存执行结果的位置。

  1. function NativeModule(id) { this.filename = id + '.js'; this.id = id;
  2. this.exports = {}; this.loaded = false;
  3. }
  4. NativeModule._source = process.binding('natives'); NativeModule._cache = {};

Javascript核心模块源文件通过process.binding('natives')取出,编译成功的模块缓存到NativeModule._cache对象上,文件模块则缓存到Module._cache对象上。

3.包与NPM

具体讲内容前,我们先看一张图:

CommonJS的包规范定义很简单,由包结构和包描述组成。

1)包结构:用于组织包中的各种文件

2)包描述:用于描述包的相关信息,供外部读取分析

3.1包结构

包实际上是一个存档文件,一个目录直接打包为.zip或tar.gz格式的文件,安装解压还原为目录。

符合CommonJS规范的包目录应该包含以下文件:

1)package.json:包描述文件

2)bin: 用于存放可执行二进制文件的目录

3)lib:用于存放JavaScript代码的目录

4)doc:用于存放文档的目录

5)test:用于存放单元测试用例的代码

Tip: 当一个包完成后,用户看到单元测试和文档时,会有一种可靠的感觉。

3.2.包描述文件于NPM

包描述文件用于表达非代码相关的信息,它是一个JSON格式的文件----package.json,位于包的根目录下,是包的重要组成部分。NPM的所有行为都与包描述文件的字段息息相关。

3.3NPM常用功能

CommonJS包规范是理论,NPM是其中的一种实践。NPM帮助第三方模块的发布、安装和依赖等。借助NPM,Node与第三方模块之间形成来很好的一个生态系统。

借助NPM,我们可以快速安装和管理依赖包。除此之外,NPM还有一些巧妙的用法。

  1. // 查看当前NPM版本
  2. $ npm -v
  3. 1.2.
  4.  
  5. // 查看帮助
  6. $ npm
  7.  
  8. // 查看具体命令
  9. npm help <command

3.4安装依赖包

npm install 安装依赖包是NPM最常见的用法,执行这个命令后,NPM会在当前目录下创建node_modules目录,然后在node_modules目录下创建express目录,接着将包解压到这个目录下。

安装完成后,在代码里面使用require()来使用就可以了。

安装有几种模式,全局模式安装、本地安装、非官方源安装。

3.5NPm钩子命令

package.json中script字段就是让包在安装或者卸载中提供钩子机制,例如:

  1. "scripts": {
  2. "preinstall": "preinstall.js",
  3. "install": "install.js",
  4. "uninstall":
  5. "uninstall.js",
  6. "test": "test.js"
  7. }

当我们执行npm install <package>时,preinstall指向的脚本将会被加载执行,然后install指向的脚本会被执行。在执行npm uninstall <package> 时,uninstall指向的脚本也许会做一些清理工作等。

当我们在某一个具体的包目录下执行npm test时,将会运行test指向的脚本。

总结

Node通过模块规范,形成了自身的原生模块。NPM通过对包规范的支持,形成了第三方模块,让我们在开发项目依赖得到很好的解决,并且提供了分享和传播的平台。借助第三方的开源力量,Node第三方模块的发展速递可谓是一步千里。

参考资源:

  1. http://www.commonjs.org
  2. http://npmjs.org/doc/README.html
  3. http://www.infoq.com/cn/articles/msh-using-npm-manage-node.js-dependence
  4. http://nodejs.org/docs/latest/api/modules.html
  5. http://addyosmani.com/writing-modular-js/
  6. http://seajs.org/docs/
  7. http://zh.wikipedia.org/zh/JavaScript
  8. http://zh.wikipedia.org/wiki/ECMAScript
  9. http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
  10. http://www.w3.org/TR/html5/
  11. http://arstechnica.com/web/news/2009/12/commonjs-effort-sets-javascript-on-path-for-world-d
  12. omination.ars
  13. http://cnodejs.org/topic/4f16442ccae1f4aa270010d7
  14. http://wiki.commonjs.org/wiki/Packages/1.0
  15. http://npmjs.org/doc/developers.html#The-package-json-File

欢迎关注公众号,进一步技术交流:

 

Node解析之----模块机制篇的更多相关文章

  1. Node.js之模块机制

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

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

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

  3. Node.js入门:模块机制

    CommonJS规范      早在Netscape诞生不久后,JavaScript就一直在探索本地编程的路,Rhino是其代表产物.无奈那时服务端JavaScript走的路均是参考众多服务器端语言来 ...

  4. 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发

    每天记录一点:NetCore获得配置文件 appsettings.json   用NetCore做项目如果用EF  ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...

  5. 深入浅出node(2) 模块机制

    这部分主要总结深入浅出Node.js的第二章 一)CommonJs 1.1CommonJs模块定义 二)Node的模块实现 2.1模块分类 2.2 路径分析和文件定位 2.2.1 路径分析 2.2.2 ...

  6. 《深入浅出Node.js》第2章 模块机制

    @by Ruth92(转载请注明出处) 第2章 模块机制 JavaScript 先天缺乏的功能:模块. 一.CommonJS 规范: JavaScript 规范的缺陷:1)没有模块系统:2)标准库较少 ...

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

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

  8. 模块机制 之commonJs、node模块 、AMD、CMD

    在其他高级语言中,都有模块中这个概念,比如java的类文件,PHP有include何require机制,JS一开始就没有模块这个概念,起初,js通过<script>标签引入代码的方式显得杂 ...

  9. Node总结 模块机制

    1. Node中的模块分为两类.一个是node提供的模块,称为核心模块,如http, fs, path:另一类是用户编写的模块,称为文件模块. 2. require()方法接收一个标识符进行模块查找. ...

随机推荐

  1. 50道高级sql练习题;大大提高自己的sql能力(附具体的sql)

    问题及描述:--1.学生表Student(SID,Sname,Sage,Ssex) --SID 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程表Course(CID ...

  2. luogu题解P1967货车运输--树链剖分

    题目链接 https://www.luogu.org/problemnew/show/P1967 分析 NOIp的一道裸题,直接在最大生成树上剖分取最小值一下就完事了,非常好写,常数也比较小,然而题解 ...

  3. nexus 匿名用户的问题。

    为了做到安全和不浪费我们自己的服务器资源,要绝对拒绝匿名用户进行访问: 1,不允许匿名用户访问 2,禁用匿名的账号 以下是这2点的设置图. ============================== ...

  4. nc 命令

    目录 nc 命令 一.简介 二.案例 1.端口扫描 2.聊天 3.文件传输 4.目录传输 5.加密网络发送的数据 6.流视频 7.克隆一个设备 8.打开一个shell 9.反向shell 10.指定端 ...

  5. 【问题】XShell连接不上Debian root用户

    类似文章:https://www.lianst.com/3231.html 修改此文件 重启ssh服务 ssh restart有问题,换一条命令OK 你的Linux发行版可能不一样,针对CentOS参 ...

  6. SQL Server 对接MySQL 数据库

    1.在SQL SERVER服务器上安装MYSQL ODBC驱动; 驱动下载地址:http://dev.mysql.com/downloads/connector/odbc/ 2.安装好后,在管理工具- ...

  7. Springboot项目中的favicon

    当项目还不大的时候,打开浏览器的favicon是自带的小叶子,如下图 此时,我们只需要将我们想要的favicon命令为favicon.ico放置在resource下,重启服务即可改变图标 当项目越来越 ...

  8. webpack 和 code splitting

    Code Splitting指的是代码分割,那么什么是代码分割,webpack和code splitting又有什么样的联系呢? 使用npm run dev:"webpack-dev-ser ...

  9. mysql修改表字段顺序

    修改字段排列位置 ALTER TABLE 表名 MODIFY 字段名1 数据类型 FIRST|AFTER 字段名2 参数说明 FIRST,可选参数 将字段1,修改为表的第一个字段. AFTER 字段名 ...

  10. IP分组

    IP 分组为了更准确地讨论 I n t e r n e t协议处理,我们必须定义一些名词.图 显示了在不同的I n t e r n e t层之间传递数据时用来描述数据的名词.我们把传输协议交给 I P ...