Node采用的CommonJS
由于CommonJS具有同步执行的特性,也就是说浏览器需要等待加载的文件从服务器拿来并执行完毕才能继续执行后面的代码,这对于浏览器加载页面非常的不利。再者CommonJS需要将代码放入到函数中执行,所以需要浏览器厂商的支持,但是浏览器厂商不愿意支持,因为CommonJS属于社区标准而非官方标准。
Node应用由模块组成,采用CommonJS模块规范。根据这个规范,每个文件就是一个模块,有自己的作用域。在这些文件里面定义的变量、函数、类,都是私有的,对外不可见,因此规避掉了作用域污染。
根据CommonJS规定,每个模块内部,module变量代表当前模块,这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实就是加载该模块的exports属性。
NodeJS的模块是运行在一个函数当中的
- 函数的参数当中依次是
exports,require,module,__filename,__dirname。
我们的代码会在这个函数中运行,这个函数会在最后返回module.exports,也就是
return module.exports;
module.exports与exports
module.exports本身是一个空对象,为了方便,node为每个模块提供了一个exports变量,指向module.exports。这等同于在每个模块头部,有这么一行代码:var exports = module.exports;所以,我们可以直接在
exports上添加属性,但是不可以将exports指向一个值,因为我们真正输出的是module.exports,这样做会切断exports和module.exports的联系。
NodeJS中,没有全局作用域,只有模块作用域
外部无法访问内部,内部亦无法访问外部
如果使用了
require('./a')这种不带文件后缀的形式,会向从当前文件所在路径找a.js,如果没有a.js则找是否有a文件夹,会将这个文件夹当做是一个包的文件夹,如果有a文件夹,会找a文件夹中的package.json,然后再找里面的main属性对应的入口文件路径。如果没有package.json或main属性,则找a文件夹中是否有index.js,如果有则直接引用index.js相对路径不能省略前面的
./可以省略后缀名
require('./a.js') 加载执行a.js文件
let temp = require('./a.js') 加载执行a.js文件 并拿到a.js导出的module.exports
require加载规则
1. 优先从缓存中加载
假设有3个模块,
main.js,a.js,b.jsmain.js先后require了a.js和b.js,a.js又require了b.js,那么main.js中的require('./b.js')就不会再执行了,而只是拿到其中的module.exports,因为main.js所require的a.js中已经require了b.js并执行了其中的代码,为了节省性能,就不会再次执行b.js了
2. 判断模块标识
- 路径形式模块
路径形式的模块,例如
./,../,/xxx(这里的/代表的是文件所在磁盘根路径),D:/xxx/xxx后两种绝对路径几乎不用,因为一旦文件到了别人机器中,这个路径很可能就是错的了路径形式的模块,后缀名可以省略,但是
./,../不可省略,例如require('./app.js')
- 核心模块
- 核心模块本质也是文件,只是他们被编译进了二进制文件中了,我们只需按照她们的名字来加载即可,例如
require('fs')
- 第三方包
NodeJS中凡是第三方包都必须通过npm下载使用時通过
require('包的名称')即可核心模块的名称和第三方包的名称不可能相同
当
Node发现require中的内容既不是路径形式模块,也不是核心模块時,会先找到当前文件所处的目录中的node_modules,然后再去找与require中的内容相同名字的文件,再去找其中的package.json文件,再找到package.json文件中的main属性,main属性的值就是这个第三方包的入口文件,然后就会加载这个入口文件如果
package.json不存在或者package.json中的main属性是错的或没有的时候,Node会默认加载该目录下的index.js文件如果以上条件都不成立,则会向上一级查找
node_modules文件,上一级如果也没有,就会继续一直向上查找,直到文件所在磁盘根目录还找不到,报错:can not find module xxx正常情况下,我们一个项目中只会有一个
node_modules文件夹,并且处在项目根目录中