Node Package Manager (又名npm)使Web开发人员可以轻松访问许多很棒的JavaScript模块,并在尝试查找和管理应用程序依赖项时使我们的工作变得更加轻松。 这也使开发人员可以轻松地创建和发布自己的模块 ,这意味着其他开发人员可以通过简单的npm install -g your-tool
来获取它们,并在需要时随时使用它们。 这是乌托邦! 对?
嗯,实际上...
我们有点问题
我永远不会说在安装npm模块时永远不要使用-g
选项,但是我不得不说,过多使用它会导致问题。 我认为有两个原因可以减少对全局模块安装的使用,尤其是在构建,测试或整理工具(例如Gulp , Karma , JSHint等)的情况下。 在整篇文章中,我将主要提到Gulp,因为它很受欢迎并且说起来很有趣,但是,如果您不喜欢Gulp,只需在心理上将其替换为您喜欢的任何东西即可。
首先,即使您的项目依赖全局模块,也不会将全局模块列为您的项目中的依赖项,这会导致其他使用您的应用程序的步骤。 您知道需要使用Gulp才能使项目准备好投入生产,因此您可以在全球范围内安装和使用它。 当其他人想要开始工作或使用您的精彩开源项目时,他们不能只键入npm install
并继续。 您最终不得不向README文件中添加说明,这些说明与
要使用此项目,请按照下列步骤操作 :
-
git clone
仓库 - 运行
npm install
- 运行
npm install -g gulp
- 运行
gulp
进行构建
我看到两个问题:首先,您添加了额外的全局安装Gulp的步骤,其次,您正在直接运行gulp
。 我看到可以避免的额外步骤(在全球范围内安装Gulp),而且我看到用户必须知道您的应用程序使用Gulp才能构建项目。 第一个问题是我在本文中要解决的主要问题,尽管第二个问题不那么重要,但是如果最终要切换工具,则需要更新说明。 我稍后讨论的解决方案应同时解决这两个问题。
与全局安装模块有关的第二个大问题是,由于安装了错误的模块版本,您可能会遇到冲突。 以下两个示例对此进行了说明:
- 您六个月前创建了项目,当时使用的是最新版本的Gulp。 今天,有人克隆了您项目的存储库,并试图运行
gulp
进行构建,但遇到了错误。 这是因为克隆您的项目的人正在运行的Gulp的旧版本或新版本存在一些重大差异。 - 您六个月前创建了一个使用Gulp的项目。 从那以后,您开始进行其他项目并在计算机上更新了Gulp。 现在,您回到这个旧项目并尝试运行
gulp
,由于自从您上次触摸该项目以来已经更新了Gulp,因此您会遇到错误。 现在您被迫更新构建过程,以与新版本的Gulp配合使用,然后才能在项目上取得更多进展,而不是将其推迟到更方便的时间。
这些可能是非常严重的问题。 就像我之前说的,我不会发表笼统的声明,告诉您永远不要在全球范围内安装某些产品。 也有例外。
安全简要说明
默认情况下,在某些系统上,全局安装npm模块需要提升的特权。 如果发现自己正在运行sudo npm install -g a-package
类的sudo npm install -g a-package
,则应更改此设置。 我们的npm初学者指南向您展示了如何。
规则例外
那么,您可以在全球范围内安装什么? 简而言之:您的项目不依赖的任何内容。 例如,我安装了一个名为local-web-server的全局模块。 每当我只有一些要在浏览器中查看的HTML文件时,我都会运行ws
(这是local-web-server的命令),它将当前文件夹设置为localhost:8000
的根目录,我可以在浏览器中打开下面的所有文档,然后对其进行测试。
我还遇到了以下情况:我想缩小不属于项目的JavaScript文件,或者至少不属于允许我建立正式构建过程的项目的一部分(出于愚蠢的“公司化”原因)。 为此,我安装了uglify-js ,可以在几秒钟内轻松地从命令行中缩小任何脚本。
解决方案
现在我们知道了问题可能出现的地方,我们如何预防这些问题? 您需要做的第一件事是在安装模块时删除-g
。 您应该将其替换为--save-dev
以便将模块另存为开发依赖项,并且在有人运行npm install
时将始终安装该模块。 那只能解决我提到的一个小问题,但这只是一个开始。
您需要知道的是,当在本地安装依赖项时,如果该依赖项具有要从命令行运行的任何脚本,则它们将放置在./node_modules/.bin/
。 因此,现在,如果仅在本地安装Gulp,则可以在命令行中键入./node_modules/.bin/gulp
来运行它。 当然,没有人想输入整个内容。您可以使用npm scripts修复此问题。
在package.json
文件中,您可以添加一个类似于以下内容的scripts
属性:
{
...
" scripts ": {
" gulp ": "gulp"
}
}
现在,您可以在要运行本地版本的Gulp的任何时间运行npm run gulp
gulp。 在检查您的PATH
之前,npm脚本将在./node_modules/.bin/
目录中寻找可执行命令的本地副本。 如果你愿意,你甚至可以通过添加传递其他参数咕嘟咕嘟--
这些参数之前,如npm run gulp -- build-dev
相当于gulp build-dev
。
令人遗憾的是,与全局使用Gulp相比,您仍然需要输入更多内容,但是有两种解决方法。 第一种方法(也可以解决我之前提到的一个问题)是使用npm脚本创建别名。 例如,您不必一定要将您的应用程序绑定到Gulp,因此您可以创建运行Gulp的脚本,但不要提及Gulp:
{
...
" scripts ": {
" build ": "gulp build-prod" ,
" develop ": "gulp build-dev"
}
}
这样,您可以缩短对Gulp的调用,并使脚本保持通用。 通过使它们通用,您可以随时透明地删除Gulp并将其替换为其他内容,而无需其他人知道(除非他们在构建过程中进行工作,在这种情况下,他们应该已经知道它并且可能应该已经成为其中的一部分)离开Gulp的对话)。 (可选)甚至在有人运行npm install
之后,您甚至可以在其中抛出postinstall
脚本以自动运行构建过程。 这将清理您的自述文件。 此外,通过使用npm脚本,任何克隆您的项目的人都应该在package.json
文件中获得有关您在项目上运行的所有进程的简单直接的文档。
除了使用npm脚本之外,还有另一个技巧可以使您使用命令行工具的本地安装:相对PATH
。 我在路径中添加了./node_modules/.bin/
,以便只要我在项目的根目录中,只要输入命令名称即可访问命令工具。 我从对我写的另一篇文章的评论中学到了这个技巧(感谢Gabriel Falkenberg )。
这些技巧并不一定能替代您想使用Gulp之类的每种情况,并且确实需要花一些工夫进行设置,但是我相信将最佳实践包括在依赖项中是最好的做法。 这将防止版本冲突(这首先是依赖关系管理器背后的主要原因之一),并将有助于简化某人选择项目所需的步骤。
超越
这可能有点过分,但是我也相信Node和npm是您项目的依赖项,它们有几个可能冲突的不同版本。 如果要确保您的应用程序适用于所有人,那么您需要某种方式来确保用户也安装了正确版本的Node和npm。
您可以将Node和npm的本地副本安装到您的项目中! 但是,这并不能使所有事情都变得井井有条。 首先,每个操作系统上的Node都不相同,因此每个人仍然需要确保下载适用于其操作系统的操作系统。 其次,即使有安装通用节点的方法,您也需要确保每个人都有从命令行访问Node和npm的简单方法,例如确保每个人都将路径添加到本地副本Node和npm的PATH
。 没有简单的方法可以保证这一点。
因此,尽管我很希望能够在每个项目中强制执行特定版本的Node和npm,但我想不出一个好方法。 如果您认为这是一个好主意并提出了一个好的解决方案,请在评论中让我们都知道。 我希望看到一个足够简单的解决方案,使其可以成为标准做法!
最后的话
希望您现在能明白将您的工具列为项目的版本依赖项的重要性。 我也希望您愿意在自己的项目中完成实施这些实践所需的工作,以便我们可以将这些实践作为标准进行推进。 当然,除非您有一个更好的主意,在这种情况下,请大声说出来,让全世界了解它!
From: https://www.sitepoint.com/solve-global-npm-module-dependency-problem/