Node -- 模块机制

JavaScript从诞生以来,就缺乏一项功能:模块。不像其他高级语言,Java有类,python有import机制,JS只能通过script标签来引入代码,显得杂乱无章。

在Node出现之前,服务器端JS基本没有市场。但是经历十多年的发展后,社区也为JS制定了相应的规范,其中CommonJS规范的提出算是最为重要的里程碑。

CommonJS规范

CommonJS规范为JS制定了一个美好的愿景:希望JS能够在任何地方运行——CommonJS规范的提出,主要是为了弥补当前JS没有标准的缺陷,以达到像Python Java具备开发大型应用的基础能力,而不是停留在脚本程序的阶段。他们希望那些用CommonJS API写出的应用可以具备跨宿主环境执行的能力。这样不仅可以利用JS开发客户端应用,还可以编写:
1.服务器端JS程序
2.命令行工具
3.桌面图形界面应用程序

这里写图片描述

上图是Node,W3C,CommonJS,ECMAScript之间的关系。

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

CommonJS的模块规范

CommonJS对模块的定义时分简单,主要分为模块引用,模块定义和模块标识三个部分。

1、模块引用

var math = require('math');

2、模块定义

在Node中,一个文件就是一个模块,将方法或者变量挂载在export对象上作为属性即可定义到出的方式。export是module的属性。

// math.js
exports.add = function () {
    var sum=0, i=0,
        args = arguments,
        l = args.length; 
    while(i<l){
        sum += args[i++]; 
    }
    return sum; 
};

在另一个文件中,通过require方法引入模块后,就可以定义属性或方法了:

// program.js
var math = require('math'); 
exports.increment = function (val) {
    return math.add(val, 1); 
};

3、模块标识

模块标识其事就是传递给require方法的参数,它必须是符合小驼峰命名的字符串,或者以. 、.. 开头的相对路径或者绝对路径。可以没有文件名后缀.js。

CommonJS的这套模块导出和引入机制,使得用户完全不必考虑变量污染。

Node的模块实现

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

核心模块部分在Node源码的编译过程中,编译进了二进制执行文件,在Node进程启动时,部分核心模块就被直接加载进内存中。
而文件模块则是在运行时动态加载。它需要完成完整的如下三个步骤:

在Node中引入模块,需要经历的三个步骤:
1、路径分析(Node在路径分析时,将会优先判断核心模块)
2、文件定位
3、编译执行

模块加载详述:

1、优先从缓存加载

Node和前端浏览器会缓存静态脚本文件一样, 也会对引入的模块进行缓存。并且,Node缓存的是变异和执行后的对象,而不是像浏览器那样仅缓存文件。

不论是核心还是文件模块,require方法对相同模块的二次加载都一律次啊用缓存优先的方式。核心模块的缓存检查会先于文件模块的缓存检查。

2、路径分析和文件定位

3、模块编译

在Node中,每个文件模块都是一个对象。它的定义如下:

function Module(id, parent) { 
    this.id = id;
    this.exports = {}; 
    this.parent = parent;
    if (parent && parent.children) {
         parent.children.push(this);
    }
    this.filename = null; 
    this.loaded = false;  
    this.children = [];
}

编译和执行是引入文件模块的最后一个阶段。定位到具体的文件后,Node会新建一个模块对象,然后根据路径载入并编译。编译成功的模块会想起文件路径作为索引缓存在Module._cache对象上,以提高二次引入的特性。

模块调用栈

各种模块之间的调用关系:

这里写图片描述

包与NPM

在模块之外,包和NPM则是将模块联系起来的一种机制。包的出现,在模块的基础上进一步组织了JS代码。

在介绍NPM之前,首先介绍Common JS的包规范。

这里写图片描述

CommonJS的包规范定义 由包结构和包描述文件两个部分组成,前者用于组织包中的各种文件,后者则用于描述包的相关信息。

1、包结构

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

完全符合CommonJS规范的包目录应该包含如下文件:
1、package.json:包描述文件
2、bin:用于存放可执行二进制文件的目录
3、lib:用于存放JS代码的目录
4、doc:用于存放文档的目录
5、test:用于存放单元测试用例的代码

2、包描述文件与NPM

package.json即包描述文件,它是包的重要组成部分,NPM的所有行为都与包描述文件的字段息息相关。

CommonJS为package.json文件定义了一些必需字段如name, description, version等,还有一些可选字段如homepage,os等。

包规范的定义可以帮助Node解决依赖包安装的问题,而NPM正是基于该规范进行了实现。

3、NPM常用功能

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

借助NPM,可以帮助用户快速安装和管理依赖包。

ADM和CDM

ADM和CDM规范都应用于前端场景。

ADM规范是CommonJS模块规范的一个延伸。它的模块定义如下:

define(id?, dependencies?, factory);

ADM模块需要用define来明确定义一个模块,而在Node实现中是隐式包装的。它们的目的是进行作用域隔离。

ADM需要在生命模块的时候制定所有的依赖,并通过形参传递依赖到模块中。

define(['dep1', 'dep2'], function (dep1, dep2) {        
    return function () {};
});

与ADM相比,CDM模块更接近于Node对CommonJS规范的定义:

define(factory);

在依赖部分,CDM支持动态引入:

define(function(require, exports, module) {
    // The module code goes here
});

require, exports, module通过形参传递给模块,在需要依赖模块时,随时调用require引入即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值