CommonJS是什么?

我们主流的前端框架vue/react都是基于node来构建的。在NodeJS出现之前,由于没有特别复杂的页面,前端是没有模块化这个概念的,而NodeJS诞生之后,它使用CommonJS的模块化规范。从此,js模块化开始快速发展。因此,我们知道,commonjs就是一个模块化的规范。目前流行的js模块化规范有CommonJS、AMD、CMD、UMD以及ES6的模块系统

至于什么是模块化?你能看到这里说明你已经工作有点时间了,这个不需要太多的解释,自行领悟吧。小编文采不行,不知道怎么解释,简单来说,一般来说,一个文件就是一个模块,这个文件内的作用域唯一,可以向外暴露变量,函数等。模块化的出现减少了代码的繁琐,利于代码复用和日后维护等等作用,实在高明!

前面我们说过,NodeJS是使用CommonJS的模块化规范。CommonJS它有四个核心的比较重要的环境变量,分别是modele, exports,require,global.  

modele, exports

如果你有一定的工作经验,那么你一定见过如下代码:

module.exports = {
//...............
}
以及
var math = require('XXXX');

module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口,不建议直接使用exports(为什么呢?只能说不熟悉的容易出错,而且使用不方便)。然后我们在其他模块需要引入的地方使用require来引入。

module.exports和exports都能导出,那么究竟这两个之间有什么不同的呢?

exports 导出实际还是使用了module.exports。因为node在内部对exports进行了赋值。

var exports = module.exports;

exports在导出的时候,需要添加属性或者方法,因为它指向的是module.exports,module.exports变量本就是一个对象,实际导出也是导出这个变量。不可以直接给exports赋值一个变量或者函数,这是错误的。。。

exports = '我是错误的导出';//错误的
exports = function(){ }; //错误的

正确的是这样:

exports.XXX = '我是错误的导出';//正确的
exports.XXX = function(){ }; //正确的

看到这里你应该知道为什么不建议使用exports暴露了吧!!

补充下,modules对象都有哪些属性:

 require

require就是加载模块的。所有加载的模块都会缓存保存在require.cache中。

使用require加载模块的时候,必须加 ./ 路径,不加的话只会去node_modules文件找。

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
// 引用核心模块时,不需要带路径
var http = require('http');

这里了解下require使用时候的内部处理流程:

1.在执行到require语句的时候,先检查是否存在这个模块的缓存;

2.如果没找到缓存,那么就会创建一个新的module实例,并且缓存下exports导出的值。如果没发现exports的模块那么会报错;

3.如果缓存存在的话,执行module.load()这个方法,去加载这个模块,读取文件内容后,使用module.compile()执行文件代码;

4.如果解析的过程中,出现异常,就从缓存中删除这个模块;

5.如果没有出现异常,最后返回这个模块的module.exports;

global

global对象是nodejs的全局对象,上边挂在了一些最基本的全局方法。

CommonJS模块的缓存

从require引入我们使用了缓存。当我们第一次加载模块的时候,node会加载他并且缓存下来,之后使用的时候就会直接从缓存内读取module.exports的值。缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。所以加载模块只会在第一次,后面如果需要重新加载可以通过清除缓存。

CommonJS的缺点

首先我们需要了解CommonJS的加载方式是同步加载的,这意味着只有前面执行完成才会继续执行。因为同步就会存在一个问题,加载的速度受到影响。Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范