设为首页 收藏本站
查看: 1067|回复: 0

[经验分享] 一起读nodejs(三)----模块(Modules)

[复制链接]

尚未签到

发表于 2017-2-22 07:38:20 | 显示全部楼层 |阅读模式
    本文是对nodejs0.8.9版本的api开发手册解读.nodejs网址
   模块(Modules)
   stability:5 -locked
   node有一个简单的模块加载机制.在node里面文件和模块是 一对一 对应的.例如,foo.js加载在同一文件夹下的circle.js模块.
   foo.js的内容:

var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is '
+ circle.area(4));
  
circle.js的内容:

var PI = Math.PI;
exports.area = function (r) {
return PI * r * r;
};
exports.circumference = function (r) {
return 2 * PI * r;
};
  
circle.js模块以导出了area()方法和circumference()方法.为了导出一个对象,需要加上指定的exports对象.
   属于模块的变量是私有的.在这个例子中变量PI就是私有于circle.js.
   模块机制是在require('module')模块中实现的.
  

  环形加载(Cycles)
  


当存在环形的require()调用时,当一个模块被返回被执行的时候,可能还没有加载完成,考虑下面这种情景:

a.js:

console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js:
console.log('b starting');
exports.done = false;
var a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js:
console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

当main.js加载a.js的时候,a.js依次需要加载b.js.在这时,b.js试着去加载a.js.为了防止一个无穷的循环(loop),一个未完成的a.js的副本的exports导出对象被返回到b.js模块.然后b.js完成加载,然后他的exports导出对象被提供给a.js模块.





当main.js加载完这两个模块的时候,他们都已经加载完.这个程序的输出应该这样子:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
如果你的程序里面有环形的模块依赖,确保制定相应的计划(意思是,确保在所有依赖的模块都加载完时在调用,避免在模块未加载完时,调用导致程序不可预期的结果).





(循环加载,require()方法有对应的机制,上面例子中b.js返回时,b.js加载的a.js没有加载完,但是当b.js加载完时,a.js就加载完了,这时候b.js里面保存的a.js也会被更新成完成的对象,其实我觉得保存的可能就是类似于a.js的引用.)

核心模块(Core modules)




node中有一些模块被编译成二进制. 这些模块在文档的其他地方有更详细的描述.




核心模块被定义在node的源码中的lib文件夹下.




如果核心模块的标识符被传入require()方法时,总是被优先加载.例如,require('http')将总是返回内置的http模块,即使当前有一个同名的模块文件夹.




文件模块加载机制(File modules)




如果准确的文件名字没有找到,node将会试图依次加载添加了.js,.json,.node等后缀名的文件.

.js文件被当作javascript文本文件解读,.json文件被转换成json文本文件. .node文件被当做已经编译好的插件模块,使用dloopen加载.(在dlopen()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。)




当一个模块的前缀是"/"时,表示一个模块文件的绝对路径.例如:require('/home/marco/foo.js')将会使用'/home/marco/foo.js'作为路径加载.




当一个模块的前缀是"./"时,表示一个模块文件的相对路径调用require().例如:为了让foo.js中的require('./circle.js')能被找到,circle.js必须和foo.js在同一目录下.




如果没有"./"或者"/"当文件前缀,这个模块不是一个核心模块,就是一个需要从node_modules文件夹中加载的模块.




如果给定的加载路径不存在,requier()方法将会抛出一个Error错误对象,并且Error的code属性被设置成:'MODULE_NOT_FOUND'.



   从node_modules文件夹加载模块(loading from node_mudoles folders)




如果传入到require()方法中的模块标识符不是本地模块,并且也不是"/","../",或者"./"开头,node则会在当前模块的父目录路径上追加/node_modules,以这个路径试图加载模块.




如果这里也没有找到,node会继续移动到更上一层的父目录,等等...直到到达根目录.




例如,文件'/home/ry/project/foo.js'中调用require('bar.js'),node会查找以下列出的路径,依次是:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js



这将允许程序本地化他们的依赖,以便不产生冲突.




   文件夹就是模块(Floder as modules)




组织程序和类库放进自己包含的目录下是很方便的,然后提供一个单独的入口指定这个类库,有三种方法把一个文件夹作为一个参数传进require()方法.




第一种方法是在根目录创建一个package.json文件,指定一个主模块.一个paceage.json的例子看起来像这样子:



{ "name" : "some-library",
"main" : "./lib/some-library.js" }
如果当前程序路径是在./some-library目录下,则require('./some-library')将会试图加载路径./some-library/lib/some-library.js.





这是node自动拓展自package.json文件的结果.




如果这个目录下没有出现package.json文件,node将会试图在这个目录加载一个index.js或者index.node文件出来.例如,如果在上面的例子中没有package.json文件,则require('./sone-library')将会试图加载:
./some-library/index.js
./some-library/index.node

缓存(Caching)




当模块被第一次加载之后就被缓存了起来,这意味着在其他地方调用require('foo')将会获得一样一样的对象被返回,如果他们扫描的是同一文件的情况下.




多次调用require('foo')不会引起模块代码被执行多次.这是一个重要的特性.有了它,部分加载完成的对象可以被返回,进而允许加载过渡期的依赖对象,甚至当他们引起环形加载.

  (上面这段话,其实就是对本文刚开始介绍的环形加载的一种说明,就是说,在node中require()方法加载对象时,可能还没加载完就返回了,但是返回的是一个索引而已,在一个node应用中,使用require()加载一个模块,都会先在缓存中查找,有就返回,没有在加载.)


  如果你想要一个模块的代码被执行多次,你可以导出一个方法,然后调用这个方法.


缓存模块提醒(Module Caching caveats)


  模块是以他们被解析出的文件名称为基础缓存起来的,因为模块可能由于调用require()方法的当前路径不一样,而导致解析出不一样的文件名称,(从node_modules文件夹加载),如果解析出不同的文件,将不能保证require('foo')总是会返回一致的对象.

module对象(The module Object)


  在每一个模块中,自由变量module是一个代表当前模块的引用.因此module.exports和exports对象是一样的.module实际上不是一个全局变量,但是内置于每一个模块中.

module.exports


  exports对象是被模块机制创建的,有时候,许多情况需要让模块成为一些类的一个实例,而使exports不可访问,为了把渴望导出的对象分配给moduel.exports对象,在例子中假设我们需要编写一个a.js模块.
var EventEmitter = require('events').EventEmitter;
module.exports = new EventEmitter();
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(function() {
module.exports.emit('ready');
}, 1000);
在另一个文件里我们需要做:
var a = require('./a');
a.on('ready', function() {
console.log('module a is ready');
});
注意,这种分配对象到module.exports对象的操作一定要立刻执行,如果放在任何一个回调函数里面,将不会生效.





x.js
setTimeout(function() {
module.exports = { a: "hello" };
}, 0);
y.js
var x = require('./x');
console.log(x.a);




转载请注明出处:http://blog.csdn.net/qq413041153/article/details/7922126

module.require(id)


  • id String
  • return:从解析出的模块中导出的对象.

module.require()方法提供了一种方式去加载一个在之前已经加载过的模块.



注意,为了实现这种方式,你必须获得一个这个模块的索引,自从require()方法返回了exports对象之后,这个模块只是在这个一段指定的代码里有效,为了使用你需要要明确的导出他.

module.id


  • String



  模块的标识符,是一个能代表模块的完整路径



module.filename


  • String



一个能代表模块的完整路径

module.loaded


  • boolean



  代表这个模块是加载完成还是正在加载中.

module.parent


  • module object



  代表加载当前模块的模块.

module.children


  • Array



  代表当前模块加载的子模块.

总结(All together...)

  为了在调用require()时获得准确的文件名被加载,使用require.resolve()方法.

  上面的代码放在一起,下面是require.resolve()方法的高级算法的伪代码:
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.node is a file, load X.node as binary addon.  STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
3. If X/index.node is a file, load X/index.node as binary addon.  STOP
LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let ROOT = index of first instance of "node_modules" in PARTS, or 0
3. let I = count of PARTS - 1
4. let DIRS = []
5. while I > ROOT,
a. if PARTS[I] = "node_modules" CONTINUE
c. DIR = path join(PARTS[0 .. I] + "node_modules")
b. DIRS = DIRS + DIR
c. let I = I - 1
6. return DIRS
从全局文件夹加载模块(loading from global folders)


  如果NODE_PATH环境变量被设置成一个以逗号分割的绝对路径列表,node如果没有在其他地方找到模块将会搜索这些路径,(注意:在windows中NODE_PATH是以分好为分隔符.)

  另外,node将会搜索一下路径:
1: $HOME/.node_modules
2: $HOME/.node_libraries
3: $PREFIX/lib/node
$HOME是用户的home目录,$PREFIX是node配置的node_prefix.





这些基本上都是一些历史遗留问题.建议你把你的依赖放进node_modules文件夹中,这样的话,加载更快,更可靠.





访问主模块(Accessing the main module)




当一个文件是直接从node执行的,那么require.main变量会设置成module对象,这意味着你可以在测试时判断一个模块是否是直接运行的.
require.main === module


例如foo.js,如果通过node foo.js运行,将会返回true,如果通过require('./foo'),将会返回false.


  因为module提供了一个filename对象,(正常情况下和_filename等价),当前程序的入口点可以通过require.main.filename获得.

附录:包管理建议(Package Manager Tips)


  理论上,require()方法被设计成一般情况下,可以满足加载若干合理的文件结构目录.包管理程序例如:dbkg,rpm,and npm,在没有修改的情况下,是可以从node Modules中加载,建立本地packages.


  下面我们给出了一些建议的目录结构:




  比方说,我们想在目录:/user/lib/node/<some-package>/<some-version>下保存一个包的指定版本,




  packages可以依赖另一个,为了安装包foo,你可能不得不安装bar包的一个指定版本.而且bar包可能还有自己的一些依赖,在某种情况下,这些依赖可能有冲突或者形成环形加载.

  自从node查找它加载的每一个模块的真是路径,然后就像上面描述的那样,在node_modules中查找他们的依赖,用下面的这种结构,这种情况很简单就可以解决:
/usr/lib/node/foo/1.2.3/ - Contents of the foo package, version 1.2.3.
/usr/lib/node/bar/4.3.2/ - Contents of the bar package that foo depends on.
/usr/lib/node/foo/1.2.3/node_modules/bar - Symbolic link to /usr/lib/node/bar/4.3.2/.
/usr/lib/node/bar/4.3.2/node_modules/* - Symbolic links to the packages that bar depends on.


因此,即使发生环形加载,或者依赖冲突,每个模块都能够获得一个他们能够使用的版本.


  挡在foo包中执行require('bar'),将会通过解析出来的路径连接字符/user/lib/node/foo/1.2.3/node_modules/bar获得指定版本的bar.然后,当bar包中执行require('quux')时,将会通过解析出来的路径连接字符/user/lib/node/bar/4.3.2/node_modules/quux获得指定版本的quux.

  此外,为了让模块的查找过程更理想,不要直接把包放在/usr/lib/node下,我们可以把他们放在/usr/lib/node_modules/<name>/<version>下.这样node就可以不用麻烦的在/usr/node_modules/或者/node_modules下查找找不到的包.

  为了确保模块在repl中是可见的,将/usr/lib/node_modules添加到$NODE_PATH环境变量中可能会起到效果.因为模块查到用到的其他模块全部是相对的,并且require()的加载也是基于真实路径的,所以,包可以放在任何地方.

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345387-1-1.html 上篇帖子: 一起读nodejs(四)----插件(addons) 下篇帖子: expressjs路由和Nodejs服务器端发送REST请求
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表