文档章节

从npm tips到express插件机制设计

i5ting
 i5ting
发布于 2015/07/08 07:47
字数 1989
阅读 1482
收藏 38

大部分时间,我们只用到npm的install,init,publish等功能,但它设计的非常好,有很多是我们不了解的

一起看一下

输入图片说明

全局命令

用nodejs来写cli工具是非常爽的,我干了不少这样的事儿

核心就是在package.json里配置

"preferGlobal": "true",
"bin": {
  "mh": "index.js"
},

即可

它的原理很简单,就是把这些命令,丢到环境变量里,等于

mh = node /npm_install_path/index.js

如果我没猜错的话是软连接实现

ln -s  /npm_install_path/index.js /bin/mh

npm link

为什么会知道它的原理呢?因为每次写cli都要发布到npmjs,然后安装,然后测试是否正确,太麻烦,如果使用测试,路径等也比较麻烦

后来发现

npm link

会把开发代码直接在本地完成上面的事儿,爽死了

link之后,会有提示

/Users/sang/.nvm/v0.10.38/bin/nmm -> /Users/sang/.nvm/v0.10.38/lib/node_modules/nmm/index.js /Users/sang/.nvm/v0.10.38/lib/node_modules/nmm -> /Users/sang/workspace/moa/nmm

如何确认它是软连接呢?

➜  nmm git:(master) ls -alt /Users/sang/.nvm/v0.10.38/bin/nmm
lrwxr-xr-x  1 sang  staff  32 Jul  7 15:38 /Users/sang/.nvm/v0.10.38/bin/nmm -> ../lib/node_modules/nmm/index.js

常见的start,test

一般我喜欢重写start和test命令,比如

"scripts": {
  "start": "nodemon ./bin/www",
  "test": "mocha -u bdd"
},

通过npm start使用nodemon来启动express服务。

通过npm test来跑mocha测试。

无论从语义还是便利性上,都是不错的。

more see https://docs.npmjs.com/cli/start

npm run

但是,npm支持命令就那么多,可能不够用,比如我要测试代码覆盖率

"scripts": {
  "start": "npm publish .",
  "test": "./node_modules/.bin/gulp",
  "mocha": "./node_modules/.bin/mocha -u bdd",
  "cov":"./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
},

很明显没有npm cov命令的,那么怎么办呢?不要急,可以通过npm run-script来搞定

上面的scripts定义,可以这样执行

npm run cov

对于自定义脚本,这样就可以解决这个问题,它的实现原理很简单,但却非常实用。

pre-commit

有的时候我们有这样的需求,在提交代码之前,做一下测试,如果

npm test && git push

这样就太麻烦了,程序员还是应该更懒一点

有没有更简单的办法呢?pre-commit

npm install --save-dev pre-commit

用法是在package.json里增加pre-commit字段,它一个数组

{
  "name": "437464d0899504fb6b7b",
  "version": "0.0.0",
  "description": "ERROR: No README.md file found!",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: I SHOULD FAIL LOLOLOLOLOL \" && exit 1",
    "foo": "echo \"fooo\" && exit 0",
    "bar": "echo \"bar\" && exit 0"
  },
  "pre-commit": [
    "foo",
    "bar",
    "test"
  ]
}

像上面的定义是在 git push之前按顺序执行foo,bar和test,也就是相当于

npm run foo npm run bar npm test git push

install

我们最常用的npm install是把node模块里文件下载安装到node_modules里面,这个很好理解,那么如果我想要自定义安装呢?

以我们上面讲的https://github.com/observing/pre-commit,它是需要先安装pre-commit脚本,这个时候该怎么办呢?

实际上我们可以在scripts自定义install命令的

"install": "node install.js",

npm install pre-commit的时候,它会下载代码,然后他会执行install脚本里的内容。也就是说在install.js里,它可以把想做的事儿做了,脚本也好,编译c扩展也好,都非常简单

再论install

我们一般写模块的时候,首先都是npm init的,然后加大量代码,比如你要加test,你可能还有examples,甚至放大量doc,这些东西,难道让装你这个npm的人都下载么?

想想就是件恐怖的事儿

npm的解决方案和git的方案一下,git是创建.gitignore,npm也照做

touch .npmignore

然后在里面放上想过滤的,不想用户安装时候下载的就好了

比较讨厌的是https://github.com/github/gitignore竟然没有

循环引用

循环引用在ios开发非常常见,即互相引用,导致无法引用计数归零,就没法清理内存,再扯就远了

看npm里,比如a模块依赖b模块,

{
  "name": "A"
  "version": "0.1.2",
  "dependencies": {
    "B": "0.1.2"
  }
}

安装完后

├── node_modules
│   └── B
├── package.json
└── README.md

如果a和b都依赖c呢?

安装后

├── node_modules
│   ├── B
│   │   ├── node_modules
│   │   └── package.json
│   └── C   
├── package.json
└── README.md

这样b能引用c,c就不用安装了

这个问题是node_modules/B/package.json里

{
  "name": "B"
  "version": "0.1.2",
  "dependencies": {
    "C": "0.0.1"
  },
  "scripts": {
    "postinstall": "node ./node_modules/C make"
  }
}

在安装b之后,不会执行c的安装了,主要是路径变量,做法很简单,判断路径即可

// node_modules/B/runMe.js
var deps = ['C'], index = 0;
(function doWeHaveAllDeps() {
  if(index === deps.length) {
    var C = require('C');
    C.make();
    return;
  } else if(isModuleExists(deps[index])) {
    index += 1;
    doWeHaveAllDeps();
  } else {
    setTimeout(doWeHaveAllDeps, 500);
  }
})();

function isModuleExists( name ) {
  try { return !!require.resolve(name); }
  catch(e) { return false }
}

如果想试试,参考http://krasimirtsonev.com/blog/article/Fun-playing-with-npm-dependencies-and-postinstall-script

这个问题并不常见,比较少,但是postinstall确实让人脑洞打开的一个东西

postinstall

如果各位熟悉mongoose的hook,一定会知道pre和post是啥意思,一般来说pre是previos之前的意思,post是之后的意思。

那么postinstall从字面上解,即安装之后要执行的回调。

看一下文档

https://docs.npmjs.com/misc/scripts

它确确实实是安装后的回调,这意味着我们可以借助npm做的更多

先看一下npm还提供了那些回调

  • prepublish: Run BEFORE the package is published. (Also run on local npm install without any arguments.)
  • publish, postpublish: Run AFTER the package is published.
  • preinstall: Run BEFORE the package is installed
  • install, postinstall: Run AFTER the package is installed.
  • preuninstall, uninstall: Run BEFORE the package is uninstalled.
  • postuninstall: Run AFTER the package is uninstalled.
  • preversion, version: Run BEFORE bump the package version.
  • postversion: Run AFTER bump the package version.
  • pretest, test, posttest: Run by the npm test command.
  • prestop, stop, poststop: Run by the npm stop command.
  • prestart, start, poststart: Run by the npm start command.
  • prerestart, restart, postrestart: Run by the npm restart command. Note: npm restart will run the stop and start scripts if no restart script is provided.

擦,太牛逼了,这货考虑的真的太全了,那么下面我们就看看如何利用npm的回调干坏事吧

express插件机制设计

大家都知道express基于connect,有middleware中间件的概念,它本身遵循小而美的设计哲学,导致它非常精简

从express@generator来看,它就只能做点小打小闹的东西,如果要设计一个复杂的大系统,就免不了和代码结构,模块,组件等战斗

从我的角度讲,这些东西都可以理解成是业务插件,比如对于一个框架来说,用户管理就应该像ruby里的devise一样,以一个gem的形式存在,如果代码里引用,调用就好了。

gem + rails plugin机制可以做,那么express + npm也是可以的,但是我们缺少的plugin机制,本文先不讲plugin机制,先说利用npm的回调实现它的可能性

比如在一个boilerplate项目里,我们安装插件

npm install --save moa-plugin-user

安装完成之后,我们需要对项目里的文件或配置也好做一个插件登记,这些东西是否可以放到postinstall里呢?

剩下的就都是nodejs代码了,大家写就好了。

如何学习

输入图片说明

https://docs.npmjs.com/

文档虽好,可是不好理解啊,而且有的时候用到了才会看

对于开发而言,代码在手,天下我有,尤其nodejs的模块都是完全开放得,您看不看它都在你的项目目录里,一丝不挂。

编码之外,看看node_modules目录,打开package.json看看,如果发现有不懂的就去查一下文档,这样效果是最好的。

看模块可以挑一些比较好,开源贡献比较多的模块

从别人的代码里学到东西,这应该是最强的学习能力,是长远的,与各位共勉。

全文完

欢迎关注我的公众号【node全栈】

node全栈.png

© 著作权归作者所有

上一篇: gulp结构化实践
下一篇: mongodb分页优化
i5ting
粉丝 198
博文 68
码字总数 98509
作品 3
东城
私信 提问
加载中

评论(7)

i5ting
i5ting 博主

引用来自“欣儿”的评论

引用来自“i5ting”的评论

引用来自“欣儿”的评论

已经下载好的模块要怎么本地安装?没看到有介绍的啊?
你说的是c写的扩展吧?

不是,就是nodejs的模块,看到的模块安装都用npm安的,不知道这样安会不会生成配置文件这些,就想问本地已经下好模块,还需要配置什么吗?
不要的,安装的时候 npm install --save xxx 会保存到package.json的dep里 如果是 npm install --save-dev xxx 会保存到package.json的dep-dev里 如果无参数,是不会保存的
欣儿
欣儿

引用来自“i5ting”的评论

引用来自“欣儿”的评论

已经下载好的模块要怎么本地安装?没看到有介绍的啊?
你说的是c写的扩展吧?

不是,就是nodejs的模块,看到的模块安装都用npm安的,不知道这样安会不会生成配置文件这些,就想问本地已经下好模块,还需要配置什么吗?
i5ting
i5ting 博主

引用来自“The-Dawn”的评论

点个赞先, 看起来不错
thanks
i5ting
i5ting 博主

引用来自“欣儿”的评论

已经下载好的模块要怎么本地安装?没看到有介绍的啊?
你说的是c写的扩展吧?
欣儿
欣儿
已经下载好的模块要怎么本地安装?没看到有介绍的啊?
--0_0--
--0_0--
点个赞先, 看起来不错
i5ting
i5ting 博主
基于express,支持插件开发的,moajs即将发布,敬请期待

* Modular && Plugable
* Scaffold
* Model-View-Controller (MVC) pattern
* Restful Api
* Auto mount routes
* Mongoosedao for data access
* Gulp as task management
* Live reload
Nodejs框架演进之路

Nodejs框架演进之路 大部分框架的演进之路大体都是一样的 实现(土方法) 实践(项目实操) 看齐最佳实践 不断封装、造轮子 走出自己的特色 下面简单的介绍一下moajs的演进之路 什么是Moajs...

i5ting
2015/12/26
255
0
OSChina 开源周刊 42 期

前端开发 【博客】React-Native 入门指南之环境配置 【博客】jenkins 简单入门使用(Web) 【博客】从npm tips到express插件机制设计 【博客】为什么 JSP 会比 Beetl 慢 【软件】ReactCSS —...

OSC编辑部
2015/07/11
2.3K
1
如何选择正确的Node框架:Express,Koa还是Hapi?

简介 Node.js是10年前首次推出的,目前它已经成为世界上最大的开源项目,在GitHub上有+59,000颗星,下载次数超过10亿。流行度快速增长的部分原因是Node.js允许开发人员在应用程序的客户端和服...

一二三OTT
04/24
0
0
Vue进阶(五十四):vue-cli脚手架build目录中的dev-server.js配置文件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sunhuaqiang1/article/details/90040898 这个配置文件是命令npm run dev 和 npm run start 的入口配置文件,主...

No Silver Bullet
05/09
0
0
【NodeJS】安装CNPM

首先建立一个文件夹用来存放Node全局的插件 比如我建立的文件夹路径为"/home/zfj/npmConfig" 设置全局安装路径 npm config set prefix "/home/zfj/npmConfig/npm" npm config set globalcon......

江江的喵
2016/02/29
857
0

没有更多内容

加载失败,请刷新页面

加载更多

【0918】正则介绍_grep

【0918】正则介绍_grep 9.1 正则介绍_grep上 9.2 grep中 9.3 grep下 一、正则介绍 正则是一串有规律的字符串,它使用单个字符串来描述或匹配一系列符合某个语法规则的字符串。 二、grep工具 ...

飞翔的竹蜻蜓
15分钟前
4
0
为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
52分钟前
8
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
今天
4
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
今天
4
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部