您的位置:首页 > Web前端 > Node.js

Node.js 项目的依赖管理

2017-12-20 21:58 232 查看


依赖管理

node 由于 npm 的存在,几乎每个项目都有一大堆的依赖模块,我们要如何维护这些依赖模块呢?


原始的解决方案:手动管理

在最初接触 node 的时候,我们通常通过 npm 安装好依赖模块,然后就把这些依赖模块和我们自己的代码推上了github,甚至还会修改这些依赖模块的代码。

我的第一个 node 项目
nae 网站,就属于这一类。不忍直视的把一部分依赖模块传上了 git(可能是因为我修改了这些模块的内容),同时其他的依赖也没有通过
package.json
统一管理。


进阶方案:package.json

当然,在摸索了一段时间之后,大部分的同学都开始知道,原来 npm 是需要和 package.json 一起玩儿的!

于是,我们开始把项目的依赖写到package.json里面,例如这个项目。于是,我们可以很方便的只需要执行:
$ git clone git[@github](/user/github).com:dead-horse/socket.io-sample.git
$ cd socket.io-sample
$ npm install


然后,依赖就按照我们在 package.json 里面写的装好了。

同时稍微细心一点的同学可能会发现,现在我的这个项目里面 node_modules 文件夹不见了,因为我把它加到了
.gitignore
文件中去了。为什么要这样做?
保持代码库的精简。
每次更新依赖的变更会污染提交之间的diff。
一些 
c++ addon
 在不同的环境和 node 版本下需要重新编译,而如果别人从代码库拉下来的代码已经有了你编译好的代码,npm
是不会重新安装它们的。


高级方案:使用项目构建工具

上面通过 package.json 管理依赖的这一套解决方案,在入门和学习的很长一段时间内可能都已经足够了。但是,世界没有这么单纯,当你真正开始使用 node 做一些实际的工作的时候,你会发现进阶方案已经不太够用了:
npm 太慢,默认 python 版本太低,各种原因导致我们安装依赖可能并不是简单的一句 
npm
install
 就可以解决的。
单元测试、覆盖率报告、压测报告,各种编译,越来越多的命令需要执行。

是时候引入项目构建工具来帮我们解决这些问题了。这次出现在我们视野中的是
GNU make
。它被广泛应用在 c 和 c++ 的项目构建之中,而我们的是 node 项目,为什么选择它?
几乎所有的服务器,肯定都需要有 c / c++ 的编译环境,所以 make 工具也会默认的出现在几乎所有的服务器上。
可以直接调用执行 shell 命令。
它具有依赖检查的功能,且语法简单。

cnpmjs.org 就是一个通过 make 来进行项目构建的 node 项目。我们稍微精简一下它的 Makefile 文件:
TESTS = $(shell ls -S `find test -type f -name "*.test.js" -print`)
REPORTER = tap
TIMEOUT = 30000
MOCHA_OPTS =

install:
[@npm](/user/npm) install --registry=http://registry.cnpmjs.org --cache=${HOME}/.npm/.cache/cnpm --disturl=http://dist.u.qiniudn.com

test: install
[@NODE_ENV](/user/NODE_ENV)=test ./node_modules/mocha/bin/mocha \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
--require should \
$(MOCHA_OPTS) \
$(TESTS)

test-cov:
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov

test-cov-html:
[@rm](/user/rm) -f coverage.html
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
[@ls](/user/ls) -lh coverage.html

.PHONY: test


基于这个
Makefile
文件,我们可以:
make install: 从 cnpmjs.org 快速安装依赖
make test: 安装所有的依赖并通过 mocha 执行单元测试。
make test-cov && make test-cov-html: 执行单元测试并通过 blanket 模块来跑项目的测试覆盖率,生成 html 格式的测试报告。


版本管理

在前面,我们已经进化到了通过项目构建工具来管理项目依赖了,但是还有一个问题我们还没有解决:依赖模块的版本。

在讲下面的东西之前,我们先来看看所有 npm 中模块版本遵循的规范:semver 2.0
所有的版本都是 
MAJOR.MINOR.PATCH
 形式。package.json 中 可以指定依赖模块为特定版本或者特定的版本范围。
1.2.3
=1.2.3
:指定版本为1.2.3
>1.2.3
<1.2.3
:大于/小于
1.2.3
>=1.2.3
<=1.2.3
:大于等于/小于等于
1.2.3
1.2.3 - 2.3.4
:大于1.2.3并且小于2.3.4
~1.2.3
:合理的靠近1.2.3,等价于 
>=1.2.3-0
<1.3.0-0
1.3.0-beta
不满足这个判断条件
~1.2
: 等价于 
>=1.2.0-0
<1.3.0-0
,所有以1.2开头的版本,同样等价于 
1.2.x

~1
:等价于 
>=1.0.0-0
<2.0.0-0
,所有以1开头的版本,等价于 
1.x

*
:任意版本

我们有了这些管理版本的限定方法,看看我们能够怎么来控制依赖的版本。


豪放派

"dependencies": {
"connect": "2.x",
"mysql": "2.x",
"redis": "*",
"debug": "*",
"eventproxy": "*",
"connect-redis": "*"
}


这一种风格,最大的好处是不太需要修改这些依赖模块的版本了。但是隐藏的风险却很大:
npm 有缓存机制,所以如果用 
*
,不能保证从 npm 安装到的是最新版本。
你本地安装完依赖,开发并测试通过了,可能生产环境安装到的依赖的版本和开发时可能不一样,一旦因此引入了隐藏的 bug,将会非常难发现。

因此,线上项目不太建议通过此种方式来管理依赖的版本。


婉约派

"dependencies": {
"connect": "~2.12.0",
"mysql": "~2.0.0",
"redis": "~0.10.0",
"debug": "*",
"eventproxy": "~0.2.6",
"connect-redis": "~1.4.6"
}


采用这种风格时,你需要跟踪你的依赖的版本,来决定你是不是要升级到新的版本。线上依赖的版本和本地依赖的版本的相差,也被限定到了最小的级别。当然还是有一定的风险。


保守派

"dependencies": {
"connect": "2.12.0",
"mysql": "2.0.1",
"redis": "0.10.0",
"debug": "*",
"eventproxy": "0.2.6",
"connect-redis": "1.4.6"
}


这种风格的好处在于,它严格的限定了版本,线上依赖和本地依赖的差异基本已经降到了最低。当然坏处也很明显,你几乎要跟踪所有依赖的版本情况,来决定是不是要升级你的依赖。


实际应用

在我们 node 的实际应用中,我们选择了第三种也就是最保守的方案,这样可以让我们尽量不会引入那些莫名其妙的bug。当然,我们是很难坚持手工去维护这些模块的版本的,经常在过了很长一段时间后,突然发现项目的依赖都已经很旧了,这些版本升级带来的 bug fix 我们都没有享受到。

我们需要一个工具来帮助我们维护项目的依赖:


autod

autod:一个自动分析项目所有的文件,获取所有的项目依赖和它们的版本的工具。

autod 同时可以根据我们传递的一些选项和参数,来直接更新 package.json 文件:
-w: 获取依赖并更新写入 package.json 文件
-e public,view: 不分析 public 和 view 中的文件
-k connect: 保持 connect 模块在 package.json 中的版本不被 autod 改变
-d nan: 无论有没有在项目中直接 require nan 这个模块,也会获取它的最新版本写入 package.dependencies 中
-r http://r.npm.taobao.net: 指定从哪个库获取这些模块的版本,默认会从 r.cnpmjs.org 获取。可以通过这个参数来设置它从内部获取。

通过这个工具,我们可以很轻松的跟踪到所有依赖的最新版本,同时可以自动更新我们的 package.json 文件,新引入的模块也不需要手动去更新 package.json 文件了,一切都可以交给 autod 来完成。




集成到 make

通常,我们会在 Makefile 中加入 autod 相关的配置项,来自动化完成这个过程:
# in Makefile
autod: install
@./node_modules/.bin/autod -w -e public,view,docs,backup
@$(MAKE) install

# in package.json

"devDependencies": {
"autod": "~0.0.11"
}


这样,我们只需要执行
make autod
,就会按照我们的配置,更新 package.json 文件,并重新安装这些模块了。

例如,我在代码里面需要引入
async
模块,这时候,只需要在代码里面:
var async = require('async');


然后在终端行执行:
$ make autod


就完成了
async
模块的安装,并将最新的 async 版本写入到了 package.json 文件中。

最后,来看看
cnpmjs.org 通过 autod 管理依赖版本的效果吧!

原文链接:http://deadhorse.me/nodejs/2014/01/18/node_dependences_version.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: