NodeJs的CommonJS模块规范
前言
本人记忆力一般,为了让自己理解《深入浅出Node.js-朴灵》一书,会在博客里记录一些关键知识,以后忘了也可以在这里找到,快速回想起来
Node通过require、exports、module实现CommonJS模块规范的
路径分析
require('http') //如http、fs、path,速度仅次于缓存加载,它在node源代码编译过程中已经被编译成二进制代码,其加载速度最快
require('./a.txt') //以.或者..开始的相对路径模块
require('/a.txt') //以/开始的绝对路径模块
//以上两种都当做文件模块来处理,在分析路径模块时require()方法会将路径转化为真实路径,并以真实路径为索引,将编译执行后的结果放到缓存中,以使二次加载更快。
//因为路径模块给了确切文件位置,所以在查找过程中可以节约大量时间,其加载慢于核心模块。
require(*) //非路径形式的文件模块,如自定义的connect模块//自己没太理解这块,因为可能没实现过自己的自定义模块所以举不出例子
//特殊的文件模块,可能是一个文件或者包的形式。这类模块查找最费时,也是所有方式中最慢的。原因是和js原型链一样要一层层node_modules找
文件定位
从缓存加载的优化策略使得二次引入不需要分析路径分析、文件定位和编译执行的过程,大大提高了再次加载时的效率
- 文件扩展名分析
require() 允许参数不带后缀,在这种情况下,node会按照.js、.json、.node次序补足扩展名依次尝试,在尝试过程中需要调用fs模块同步阻塞式判断文件是否存在。
所以在后两种引入方式时推荐加上后缀名
- 目录分析和包
require() 通过分析文件扩展名之后,可能没有查找到对应文件,但却得到一个目录,这在引入自定义模块和逐个模块路径进行查找时经常会出现,此时node会将目录当做一个包来处理。
在这个过程中,node对commonjs包规范进行了一定程度的支持。首先,node在当前目录下查找package.json,通过json.parse解析出包描述对象从中取出Main属性指定的文件名进行定位。
如果文件缺少扩展名,将会进入扩展名分析的步骤。
而如果main属性指定的文件名错误或者压根没有package.json文件,node将会将index当做默认文件名,依次添加扩展名查找
如果在目录分析的过程中没有定位成功任务文件则自定义模块进入下一个模块路径进行查找。如果模块路径数组都被遍历完毕,依然没有查找到目标文件,则会抛出查找失败异常。
编译执行
在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会新建一个模块对象,然后根据路径载入并编译。对于不同扩展名,其载入方式不一样如下:
.js 通过fs模块同步读取文件后编译执行
.node 这是通过c/c++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件
.json 通过fs模块同步读取后,用JSON.parse解析返回结果
// Native extension for .json
Module._extensions['.json'] = function(module, filename) {
var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
};
- 其余拓展名文件 都被当做js文件载入
每一个编译成功的模块都会将其文件路径作为索引缓存在Modules._cache对象上,以提高二次加载速度
实践
我们有个area.js文件看看node的commonjs规范流程
- 暴露文件给node并会给下面js包装
var math = require('math');
exports.area = function (radius) {
return Math.PI * radius * radius;
};
变为
(function (exports, require, module, __filename, __dirname) {
var math = require('math');
exports.area = function (radius) {
return Math.PI * radius * radius;
};
});
这样每个模块文件之间都进行了作用域隔离。包装后的代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是有明确的上下文,不污染全局),返回一个具体function对象。
最后将当前模块对象的exports属性、require方法、module(模块自身)以及在文件定位中得到的完整文件路径和文件目录作为参数传递给这个function执行,执行后模块的exports属性返回给了调用方,
其他变量方法无法被调用。
- 外部文件引用
require('./area');
node通过上述的(路径分析)来查这个'./area',通过fs找到area.js文件(文件定位)并读取编译执行(编译执行)
AMD与它的区别
AMD需要用define明确定义一个模块,而在node实现中是隐形包装的,目的是作用于隔离,仅在需要时被引入,避免掉过去那种全局变量或者命名空间的方式,防止被污染,另一个区别是内容需要通过返回的方式实现导出。
define(id?, dependencies?, factory);
define(function() {
var exports = {}; exports.sayHello = function() {
alert('Hello from module: ' + module.id); };
return exports; });
CMD与它的区别
AMD需要在声明模块的时候定义所有依赖,通过形参传递到模块内容中,CMD支持动态引入依赖,require、exports、module通过形参传递给模块,在需要依赖模块时,随时调用require引入即可
define(['dep1', 'dep2'], function (dep1, dep2) { return function () {};
});
define(function(require, exports, module) {
});
NodeJs的CommonJS模块规范的更多相关文章
- CommonJs模块规范
1.什么是模块化 文件作用域 通信规则 加载 require 导出 exports 2.CommonJs模块规范 在Node中的Javascript还有一个很重要的概念:模块概念 模块作用域 使用re ...
- "浏览器端" 使用 commonjs 模块规范开发网页应用,像开发 node 那样开发网页应用
Containjs 1.0 Containjs 是什么? Containjs 是一个基于 Commonjs 模块管理规范的 浏览器端 的 JavaScript 模块加载器(目前为非标准的,代码会持续迭 ...
- commonJS模块规范 和 es6模块规范 区别
ES6 模块与 CommonJS 模块的差异 CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口. Commo ...
- CommonJS 模块规范 1.1.1
本规范致力于描述一类可以同时适用于客户端和服务器端的模块系统.该系统中的模块拥有自己的作用域,可以从其他模块导入单例对象,或者对外提供 API. Require require 是一个函数对象. re ...
- CommonJs模块化(nodejs模块规范)
1.概述: Node应用由模块组成,采用CommonJS模块规范. 根据这个规范,每个文件就是一个模块,有自己的作用域.在一个文件里面定义的变量.函数.类,都是私有的,对其他文件不可见. 如果想在多个 ...
- node (02 CommonJs 和 Nodejs 中自定义模块)顺便讲讲module.exports和exports的区别 dependencies 与 devDependencies 之间的区别
CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准的缺陷.它的终极目标就是:提供一个类似 Python,Ruby 和 Java 语言的标准库,而不只是停留在小脚本程序的阶 ...
- Node.js学习笔记(二) --- CommonJs和Nodejs 中自定义模块
一. 什么是 CommonJs? JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器. 然而, JavaScript标准定义的 API 是为了构建基于浏览器的应用程序.并没有制定一 ...
- 理解CommonJS ,AMD ,CMD, 模块规范
参考 : https://blog.csdn.net/xcymorningsun/article/details/52709608 1.CommonJS 模块规范 (同步加载模块): var math ...
- Node基础-CommonJS模块化规范
1.在本地项目中基于NPM/YARN安装第三方模块 第一步:在本地项目中创建一个"package.json"的文件 作用:把当前项目所有依赖的第三方模块信息(包含:模块名称以及版本 ...
随机推荐
- tailf 跟踪日志文件
1.命令功能 tailf 跟踪日志文件增长,作用跟tail –f相同.tailf将输出文件的最后10行,然后等待文件增长. 2.语法格式 tailf option file 参数说明 参数 参数说 ...
- Mysqldump导入数据库很慢的解决办法
https://blog.csdn.net/xizaihui/article/details/53103049 1.MySQLdump导出的SQL语句在导入到其他数据库的时候会相当慢,甚至几十秒才处理 ...
- Git常用命令的操作
Git命令 一.创建版本库 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 使用命令git add <file>,注意,可反复多次使用,添加多个文件: 使 ...
- selenium 自动化的坑(3)
一天一坑系列(3) 今天不讲我是怎么定位了吧,今天讲的是关于弹窗的. 基于业务,一键全否之后需要二次确认,会弹出提示框,你会不会认为这是alert弹框?经过仔细查看元素,确认不是弹框,明明是div嘛, ...
- [洛谷 P1377] TJOI2011 树的序
问题描述 众所周知,二叉查找树的形态和键值的插入顺序密切相关.准确的讲:1.空树中加入一个键值k,则变为只有一个结点的二叉查找树,此结点的键值即为k:2.在非空树中插入一个键值k,若k小于其根的键值, ...
- 【leetcode】1051. Height Checker
题目如下: Students are asked to stand in non-decreasing order of heights for an annual photo. Return the ...
- linux运维、架构之路-linux定时任务
1.基础优化之开机启动服务优化 使用awk拼接的方式 [root@cache01 ~]# chkconfig |egrep -v "crond|network|sshd|rsyslog|sy ...
- redis学习 --Sorted Set
一.概述: Sorted Set(有序集合)和Set类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中.它们之间的主要差别是Sorted Set中的每一个成员都会有一个分数(sc ...
- python全栈开发,Day41(线程概念,线程的特点,进程和线程的关系,线程和python理论知识,线程的创建)
昨日内容回顾 队列 队列:先进先出.数据进程安全 队列实现方式:管道+锁 生产者消费者模型:解决数据供需不平衡 管道 双向通信,数据进程不安全 EOFError: 管道是由操作系统进行引用计数的 必须 ...
- jsp文件断点上传
之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...