Node应用是由模块组成的,Node遵循了CommonJS的模块规范,来隔离每个模块的作用域,使每个模块在它自身的命名空间中执行。
CommonJS规范的主要内容:
模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。
CommonJS模块的特点:
(1)所有代码运行在当前模块作用域中,不会污染全局作用域 (2)模块同步加载,根据代码中出现的顺序依次加载 (3)模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
一个简单的例子:
//demo.jsmodule.exports.name = 'Aphasia';module.exports.getAge = function(age){ console.log(age)};//需要引入demo.js的其他文件var person = require('./demo.js')根据CommonJS规范,每一个文件就是一个模块,在每个模块中,都会有一个module对象,这个对象就指向当前的模块。module对象具有以下属性:
(1)id:当前模块的bi (2)exports:表示当前模块暴露给外部的值 (3)parent: 是一个对象,表示调用当前模块的模块 (4)children:是一个对象,表示当前模块调用的模块 (5)filename:模块的绝对路径 (6)paths:从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录 (7)loaded:一个布尔值,表示当前模块是否已经被完全加载
示例:
module.js文件
执行node module.js文件
从上面的例子我们也能看到,module对象具有一个exports属性,该属性就是用来对外暴露变量、方法或整个模块的。当其他的文件require进来该模块的时候,实际上就是读取了该模块module对象的exports属性。
简单的使用示例
module.exports = 'Aphasia';module.exports.name = 'Aphasia';module.exports = function(){ //dosomething}module.exports = { name: 'Aphasia', getAge: function(){ //dosomething }}为什么还有个exports变量呢?一开始我也很郁闷,既然module.exports就能满足所有的需求,干嘛还要个exports呢。其实,原因很简单–就是为了简单。当然,二者之间还存在一些差异,听我慢慢道来。
(1)exports其实是指向module.exports,也就是相当于
exports = module.exports;(2)我们可以将二者的关系可以理解成这是一种引用关系。由于这样的引用关系可知,既然exports始终指向的是module.exports,那我们就不能给exports直接进行赋值操作(不能乱指),因为这样的操作会导致exports不再指向module.exports。
//以下操作都是不允许的exports = 'Aphasia';exports = function(){ console.log('Aphasia')}//这些操作都是合法的exports.name = 'Aphasia';exports.getName = function(){ console.log('Aphasia')}(1)exports用来抛出一个接口,require就是用来引入这个模块抛出的接口,返回引入模块的exports对象,如果没有找到该模块,就会报错。 (2)使用require引入的文件,文件名后缀默认为.js
既然是用来引入一个模块,就需要有一定的查找规则以确定需要引入的模块究竟存在哪里。
根据上面的流程图,其实可以看出以下几点: (1)被引入的模块优先从缓存中加载,命中缓存直接加载;未命中,之后会被加入缓存。 (2)如果不是node的原生模块,则根据传入到require中的参数引入模块。这一过程的查找规则如下:
如果路径是以/
开始的绝对路径,就从该项目的绝对路径中去加载该模块。比如:require(“/demo/lib/api.js”),就会从/demo/lib/api.js
中去加载该模块 如果路径是以./
开始的相对路径,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require(‘./circle’)将加载当前脚本同一目录的circle.js 新闻热点
疑难解答