译者:LuChunYu
Node.js 为开发者带来了一场革命,开发者可以编写直接在机器上运行的代码。我们的技能不再受限于浏览器了。最初,很多人仅认为它是一种不需要学习其他语言就可以开发应用服务的语言,但是很快就意识到可以给命令行写工具,使得开发周期中的许多事情自动化。
npm 是 Node.js 的包管理工具,更易快速上手使用他人开发的工具,在机器上安装了这些工具,在系统的任何位置都可以使用。JavaScript 终于成为“真正”的编程语言。但这些新功能也带来了很多最佳实践有待发现,毕竟很多新情况在浏览器中是看不到的。特别地,我想提出一种做法,最近我在反复思考,觉得社区也应该多讨论。
问题是什么? Link
特别想提使用 npm install -g来npm 全局安装包,不要误解:全局安装包有时确实很有用、很方便,但我们并不是总能恰当地使用它。
经验法则:如果你的项目依赖某个包,它应该作为依赖列入 package.json 文件中,并在项目中本地安装而不是全局安装。项目中不依赖的工具可以全局安装。例如:当文件不是整个项目的一部分或者想要分享一个文件时,使用 UglifyJS 作为全局安装包来处理 JavaScript 文件的一次性压缩。另一个好的例子是 http-server 包,它可以通过简单的命令在任意目录下启动一个简单的文件服务器。
如果你开发内部项目,可能侥幸逃脱全局包的使用,因为很多工具(例如 Docker)可以通过自动化处理一些全局包要解决的问题。如果你开发公开或者开源的项目,请特别注意,因为你是这篇文章的主要目标读者。
为什么不应该全局依赖安装呢? Link
显而易见的答案是项目依赖它们。如果项目依赖一个包,它应该记录在 package.json 中,这样才能保证别人输入 npm install 后一定会安装它们。 否则,在 README 文件中需要额外的步骤告知克隆项目的人也需要安装每一个全局依赖。
例如,如果你的项目依赖 Browserify(从这开始例子中均使用 Browserify ),那么需要在 README 文件中写一些步骤来帮助其他人启动你的项目:
根据如下步骤使用该项目:
git clone项目.- 执行
npm install. - 执行
npm install -g browserify. - 执行
browserify main.js > bundle.js来创建.
**为什么强制用户进行额外的全局安装 Browserify ?**除了确认 Browserify 已经安装, 将其加入 package.json 的依赖列表也是确保其安装了正确的版本。依赖的版本错误与完全没有安装依赖没有差别。这意味着 README 中需要有 Browserify 和其他要用到的全局包的版本号(不确定之前见过的都这样做了)。这也意味着如果你奖这些包中的任何一个更新到最新版本,就需要更新 README 中的版本。
最终,即使为项目安装了 Browserify 的正确版本, 相同的工具在不同的项目中也需要不同的版本,这样将会造成冲突。自己的某些项目中也可能使用不同版本的 Browserify,因为当开始新项目时就会更新 Browserify 的版本,不用回退来确保之前的项目更新版本后仍可以工作。这些冲突是可以避免的。
如何来实现呢? Link
显然,就是安装包的时候不要再用 -g 了,改为使用 -S 或 --save 把它们保存到依赖列表,或者使用 -D 或 --save-dev 把它们保存到开发依赖列表。当然,这个答案不完整。因为它不能解决在命令行中运行 Browserify 之类包的问题,毕竟这才是最初全局安装这些包的原因所在。如果不能解决最初的问题,那这个答案就不能算是答案,对不?
不用担心,正如所述,这不是完整的答案。目前,已经解决了版本冲突、缩减步骤和维护 README 文件的问题。提出最好的解决方案之前,需要知道个重要的事实:如果在本地安装的包中有“二进制”文件(可以在命令行执行的文件),那么执行工具所需的这些二进制文件会保存在 ./node_modules/.bin 中。这意味着你可以使用 ./node_modules/.bin/browserify 来执行本地安装 Browserify。当然,没人愿意输入中间那些没用的东西,但这是个开始。
快速解决方案是将 ./node_modules/.bin 加入到环境变量中,这样就可以运行 browserify 来工作了。首先,当头一击被告知路径需要使用相对路径(感谢我另外一篇文章的评论),但导致心情低落是由于意识到了只有在项目根目录才能工作。我想到的最好的变通方法是PATH 可以抛出一些入口,可以使用分目录来这样做(../node_modules/.bin/ 和 ../../node_modules/.bin/等等,根据需要的多级目录)。那么,就可以找到所需要的bin目录。注意使用相对路径有安全风险,因此只能在开发机上这样使用。
在自己的开发机上修改PATH环境变量没问题,因为不用再多敲键盘了,可告诉别人也去修改他们的PATH就不太好了。最终方案是为每个项目做一些设置,但这些后续会相当有用,特别是对项目的其他使用者: npm scripts。在 package.json 文件中,你可以通过 scripts 属性给你的命令创建 npm 可以运行的别名。看下 package.json 文件:
{
…
""scripts"": {
""browserify"": ""browserify""
}
…
}
运行 npm run browserify将会运行项目中本地安装 Browserify 的版本。属性的 key 是创建的别名用于 npm run (比如 npm run $KEY),它是可执行的命令。这样执行时,npm 会优先在 ./node_modules/.bin/ 文件目录中寻找 browserify 然后再到 PATH 中其他的目录中查找。
当然,输入 npm run browserify 不如 browserify 速度快,但我通常也不那样使用 npm scripts。相反,我这样设置后别人无需知道我使用了 Browserify,创建一个普通的别名并封装大量命令。例如:
{
…
""scripts"": {
""build"": ""browserify main.js > bundle.js""
}
…
}
现在执行npm run build,这样所有人都知道自己在构建项目,又不用知道细节,而且还不用多敲键盘。封装使我们完全的改变工具和编译的结构(例如webpack),不用告诉任何人,也不用更新文档。
npm scripts 也允许给命令传递其他的参数,执行时以 -- 来告知 npm 其他的参数并传给正在执行的命令,而不是直接传递给 npm run。例如使用创建的 build script,可以运行npm run build -- --debug等同于执行browserify main.js > bundle.js --debug。
npm scripts 是处理常见任务的好用工具易于发现和运行,其他人也很好上手。甚至可以不知道细节直接学习怎样使用 npm 作为“build tool”,这将越来越流行。我将“build tool”用引号标记是因为,从技术上来讲,它是项目中的命令的别名,但人们更倾向于称之为编译工具。
扩展路径和/或者利用 npm scripts 方式比全局安装工具的方式更符合当前的趋势,但我坚信这是很好地实践并很长一段时间将会很好处理维护和兼容的问题。项目的早期使用者也不会遇到问题。
要采取进一步的措施么? Link
当你完全地理解它,会意识到 Node.js 和 npm 也是你项目的依赖,也会造成版本冲突。因此,应该列出这些依赖确保项目都能正常工作且不会意识到冲突的存在。 这样,可以在项目中安装可移植的或者复制本地的 Node.js 和 npm。这有它要注意的东西。因为不想在版本控制中存储 Node.js 的安装。即使这样做,安装好Node.js也可能不会执行,因为不同的电脑有不同的操作系统。
也可以调整路径使用本地的 Node.js 和 npm ,用户同样需要这样做。对我而言,这都不是很好的解决方式,在我看来,还没有找到很简单的解决问题的方法。也许一些工具未来会允许合并,但现在不得不将这种想法暂且放下。
结论 Link
这是今天所讲的全部内容。希望大家了解项目保留依赖列表和版本的重要性。如果同意我的观点,请当看到项目中没有履行这个原则时指出并推动这个观点。记住就很好了。实际上,可以考虑通过pull请求来修复项目中的问题。