一.模块的分类
A类:核心模块 ,如http fs path等
B类:. or .. 开头的相对路径文件模块
C类:/开头的绝对路径文件模块
D类:非路径形式的文件模块,如自定义的模块
二.模块的定位
优先缓存
A类模块是node源码编译过程已经编译好的,加载最快,node启动时候会加载到缓存
B类相对路径的会在分析路径的时候转为真实路径,将真实路径作为索引,编译执行后的结果放到缓存
C类略
D类从node_modules中进行查找,这里我们写一个小demo看下查找路径:
$ vim hello.js
输入:
console.log("Hello World");
console.log(module.paths);
$ node hello.js
通过结果集可以看到查找路径是逐级递归向上一直到根目录
值得说的是扩展名:
建议最好require模块的时候都带上扩展名,因为 node 会按照.js .json .node的次序补足扩展名。
但是这个过程会调用fs模块同步阻塞判断文件是否存在,会引起性能问题。因此引用非.js文件最好带扩展名。
目录分析和包
node在当前目录下查找package.json 解析包描述对象,从中获取main属性指定的文件名进行定位,如果没找到会用index作为默认文件名。
三.模块的编译
.js的编译:fs模块同步读取文件后编译执行
.node:c/c++编写的,通过dlopen()加载执行
.json:fs模块同步读取文件,JSON.parse解析返回结果
其余扩展名:被当作js文件载入
js文件在编译的时候,node会将文件内容包装到下面这个函数中:
(function (exports,require,module,__filename,__dirname){
//js模块文件代码.
});
因此,我们可以在模块文件中使用这5个变量:
__filename:完整文件路径
__dirname:文件目录
exports:该属性上的任何属性和方法都可以被外部调用,但是模块中其余的变量和属性则不可以。因为exports属性是要被返回给调用方的。
module:模块对象自身
require:引用模块
四.模块调用栈
文件模块就是我们上述一直提及的,开发人员自己编写的模块文件,图中分成 js模块和c/c++扩展模块,其他只有一些场景需要提高性能等原因才会考虑采用,比如位运算。
核心模块分为js核心模块(我们上文指的核心模块)和内建模块,我们程序一般不会直接调用内建模块,而是通过已经用js核心模块封装的模块。当然也是有方法去直接调用的 process.binding('moduleName')
这边的东西我说的很少,大家感兴趣的可以看“深入浅出nodeJS 第二章”
五.包与npm
包和NPM是将模块联系起来的一种机制。
包实际上是一个存档文件,即一个目录直接打包为.zip 或者 tar.gz格式,安装后解压还原为目录。符合CommonJS规范的包目录应该包含如下文件:
package.json:包描述文件
bin:可执行二进制文件目录
lib:存放js代码的目录
doc:存放文档的目录
test:存放单元测试用例的代码
NPM是Node包管理工具,帮助完成第三方模块的发布,安装,依赖等。npm的行为与package.json包描述文件有关,npm世纪需要的package.json中定义的字段重要有:
----可以参考看这个帖子:[http://www.cnblogs.com/tzyy/p/5193811.html#_h1_2]
name:包名
version:版本号,major.minor.revision格式
description:包简介
keywords:关键词数组,npm用来做分类搜索
repositories:托管源代码的位置列表
author:包作者
bin:一些包作者希望包可以作为命令行工具使用,配置bin后,可以通过npm install package_name -g命令将脚本添加到执行路径中,可以命令行中直接执行。
main:模块A使用require()引入包B时,会优先检查包B的package.json中此字段,并将其作为包中其余模块的入口,如果查找失败,会查找index关键词。
scripts:主要被包管理器用来安装,编译,测试和卸载包。
engines:支持的js引擎列表,有效的引擎值包括 ejs,flusspferd,gpsee,jsc,spidermonkey,narwhal,node,v8
dependencies:使用当前包需要依赖的包列表,npm会根据这个属性帮助自动加载依赖的包
devDependencies:一些模块只在开发时需要依赖,配置这个属性可以提示包的后续开发者安装依赖包。
NPM Demo(express框架的包文件)
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.3.4",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [{"name": "TJ Holowaychuk","email": "tj@vision-media.ca"},{"email": "aaron.heckmann+github@gmail.com"}{"name": "Ciaran Jessup","email": "ciaranj@gmail.com"},{"email":"rauchg@gmail.com"}],
"dependencies": {
"connect": "2.8.4",
"commander": "1.2.0",
"range-parser": "0.0.4",
"mkdirp": "0.3.5",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.1.0",
"methods": "0.0.1",
"send": "0.1.3",
"cookie-signature": "1.0.1",
"debug": "*"
},
"devDependencies": {
"ejs": "*",
"mocha": "*",
"jade": "0.30.0",
"hjs": "*",
"stylus": "*",
"should": "*",
"connect-redis": "*",
"marked": "*",
"supertest": "0.6.0"
},
"keywords":["express","framework","sinatra","web","rest","restful", 2"router","app","api"],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": {"express": "./bin/express"},
"scripts": {
"prepublish": "npm prune",
"test": "make test"
},
"engines": {"node": "*"}
}
六.npm仓库
相对于命令行中执行npm命令,NPM仓库是存放模块的服务器。
Node在GitHub上托管源代码,在NPM上发布模块,在代码中使用第三方模块包。
npm仓库设计基于CouchDB实现。
1.安装erlang & CouchDB
因为couchdb是基于erlang实现的,因此要先安装erlang:
brew install erlang
erl #验证是否安装成功
安装CouchDB
brew install couchdb
couchdb & curl http://127.0.0.1:5984/ #查看是否安装成功
#如果以前装过,在安装之前要卸载
brew remove --force openssl erlang couchdb icu4c spidermonkey nspr
#浏览器查看数据库
http://127.0.0.1:5984/_utils/
2.搭建NPM仓库
#调用couchDB接口创建一个数据库,所有模块将作为附件保存在这个数据库中
#数据库命名规则:Only lowercase characters (a-z), digits (0-9), and any of the characters _, $, (, ), +, -, and / are allowed
curl -X PUT http://127.0.0.1:5984/cat_registry
#刷新浏览器查看数据库建立情况
#获取npm仓库源代码
mkdir npmware
git clone https://github.com/isaacs/npmjs.org.git
cd npmjs.org
--有个问题,这个代码仓库后续如何使用?
#安装工具
#couchapp简介(百度百科)
#[CouchDB](http://baike.baidu.com/view/2024470.htm)
#CouchApp 是一个开发使用的 Web 应用的小型框架。它的主要功能是可以把一个文件系统的目录转换成 CouchDB 中的一个设计文档。在开发的时候,可以按照一般 Web 应用的结构来组织文件系统,当需要测试和部署的时候,只需要一条命令就可以把该目录保存到 CouchDB 中。
npm install couchapp -g
#执行如下语句会在当前目录下生成node_modules目录,然后安装下面两个模块
npm install couchapp #在当前代码库中安装
npm install semver #版本检查参考:https://docs.npmjs.com/misc/semver
#装载NPM仓库代码到CouchDB
couchapp push registry/app.js http://127.0.0.1:5984/cat_registry
#app.js会报错,因为还没搞清楚是为什么,我就将报错的部分注释了,然后能够成功提交,图1是报错,图2是代码注释,图3是提交成功。
访问查看上传成功:http://127.0.0.1:5984/_utils/document.html?cat_registry/_design/scratch
七.局域npm库的使用
npm install ** #默认从全局npm仓库拉取
#从我们刚刚新建的局域库拉取
npm install plusplus --registry=http://127.0.0.1:5984/cat_registry/_design/scratch/_rewrite
#这边要插播一个修改couchdb的配置文件的地方,当你访问下面的网址,会返回“insecure_rewrite_rule too many ....”
http://127.0.0.1:5984/cat_registry/_design/scratch/_rewrite
ok,我们修改一下couchdb的配置文件,来解决这个问题:
[参考:http://blog.csdn.net/nsrainbow/article/details/35989657/]
vim /usr/local/etc/couchdb/local.ini
新增:
[httpd]
secure_rewrites = false
bind_address=0.0.0.0
ps -ef | grep couchdb #第二列pid
kill -9 pid
couchapp #重启
ok,下面来访问一下吧(127.0.0.1只能本机访问,换成0.0.0.0就可以被外部机器访问了):
http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite
上述的命令很长,不容易记忆,这边我们可以使用如下方式让命令更简单一些
npm config set registry http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite
#修改~/.bashrc 新增一条匿名,酱紫就可以区分官方仓库和本地仓库
alias lnpm = 'npm --registry=http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite'
八.上传发布自己的npm包
#首先创建一个自己的github repo,访问网址进行注册
https://github.com
#注册后,你会有一个自己的空库,然后按照github提示的命令在本机进行操作
git clone https://github.com/gongziLiu/lgz.git #克隆一个仓库
cd lgz
touch README.md
git add README.md
git commit -m 'my first commit'
git push -u origin master
#创建新的分支用来存我们这次新建的npm包代码,如图
#新建分支
git branch myfirstnpm
#创建couchdb账号
npm adduser --registry=http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite
#发布
npm publish --registry=http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite
但是我发现npm publish 并没有帮我们git push 分支,所以还要我们自己手动push上去。
ok,下面验证一下在客户端包的安装
npm install testmynpmpublish --registry=http://0.0.0.0:5984/cat_registry/_design/scratch/_rewrite