cnpm 安装全局模块引发的思考

cnpm 安装全局模块引发的思考

起因

因为工作的时候,公司提供的是 Windows 台式机,因此一般都是在 Windows 环境下开发的。

但是最近在用 cnpm 安装脚本的时候,忽然发现一个很有意思的问题:用 cnpm 安装的全局脚本,比如 vue-cli 居然只能在 cmd 中运行,无法在 powerShell 中运行。

类似的问题,其实以前也碰到过,但是以前一般都是报着能用就用,不去深究原理的想法。

其实很多时候,自己内心深处还是有一个声音不断地在提醒自己:“知其然更知其所以然方能走的更远”!

但是奈何,自己实力不允许,对很多东西都是一知半解,没有形成一套体系,没有拥有看透问题本质的能力。

但是现在,开始找到感觉了,于是现在碰到问题,多花点时间,稍微深究一下,事后收获还是很大的。

闲话不多说,转回我们的正题吧。

npm、cnpm、yarn 选择问题

至于为什么会用 npm,我想前端的同学应该都深有感触,特别是国内的前端同学。

我想或多或少都会经历过一下场景:

同事1:反正大家都说 npm 不好用!
同事2:npm 垃圾玩意儿,下东西太慢了!
同事3:cnpm 下东西快,比 npm 强一万倍!
同事4:你竟然还在用 npm,太老土了!
。。。

类似的情景对话,我想大家应该都经历过。

但是究竟为什么 npm 不好用,cnpm 好用呢?

这种踩 npm 的情况,是从什么时候开始的,现在还是这样吗?

npm 跟 cnpm 的差别是什么呢?

npm 可以通过什么方式变得跟 cnpm 一样好用吗?

其实这些问题,并没有多少人去深究,特别是对于很多其前端初级选手来说,估计很多人都下意识的认为,其实这两个是一个东西,都是下 nodejs 模块的嘛。

估计很多人都这样想:这些问题关我鸟事,我研究的那么透彻,老板也不会给我涨工资啊!我前端,写好页面就行了,这些个框架,能用好就行了,我开车,干嘛要了解汽车的原理呢!

如果你符合以上的思维,那么请及时中断往下看的念头吧,这篇文章不适合你。

那么究竟为什么大家都重口一词的说 npm 不好用 cnpm 好用呢?

原因是就是,以前 npm 真的挺不好用的!

这篇博主的文章分析的非常好,有兴趣的同学可以阅读一下:https://blog.xgheaven.com/2018/05/03/npm-to-yarn-to-npm/

就因为 npm 不好用,所以才催生出像 cnpm、yarn 等第三方包管理系统。

但是随着 npm 6.x 版本的发布,这些问题已经被被解决了,已经被扫进了历史的垃圾堆里了。

所以结论就是:现在用 nodejs 的包管理系统,首选 npm,实在不能用 npm 的情况下,再考虑用第三方的包管理系统。

比如 create-react-app 这个脚手架,就是只支持 yarn 的,但是如果你非要用 npm,就很麻烦了。

但是有的童鞋会发问,npm 安装模块太慢了,不能忍。

说实话,我也忍不了,忍不了你就改个 npm 模块的源呗,从镜像站去下载模块不久快很多么。

这个问题其实不止是出现在 npm 上,很多包管理系统都会出现在这样的问题。

比如 python 的 pip,Ubuntu 的 apt-get,homebrew,centos 的 yum 等等,都会因为官方源服务器在国外,访问起来太慢。

但是这个问题其实是有解决方案的,换成国内的镜像源就能解决。

比如 npm 换成淘宝的国内镜像源就能解决下载过慢的问题了,下面是配置方式:

# 换源
npm config set registry https://registry.npm.taobao.org

# 检查是否改成功了
npm config get registry

第三方包管理工具

再聊聊为什么会出现第三方包管理工具。

其实之前就说过了,因为 npm 在开始的时候不好用,所以后来社区就诞生出了更优秀的包管理工具。

但是 npm 也是在进步的,我们不能总是以一种陈旧的眼光去看待问题,以一种拥抱未来的姿态去剔除我们思想中的偏见成分。

npm 的这种历史,有点类似于 JavaScript 的发展历史。

以前 JavaScript 很多地方用起来不友好,所以催生出了很多优秀的 JavaScript 框架,十年前,风头最盛的大概就是 jQuery 了吧。

甚至一度,有人豪言壮语的宣称:不用学 JavaScript 了,学了 jQuery 就行了。

但是历史总是这么惊人的相似,随着 es6 以及后续版本的出现,jQuery、lodash 等很多增强 JavaScript 语言功能的框架都渐渐的开始退出了历史的舞台了。

至于原因,我想大家因该也知道,同样实现一种功能,自带的肯定是更好用的,如果不好用,只能说明他还有提升的空间。

这个定律,在很多时候都是比较符合现实的表现的。

所以,为什么说代码开源,有助于计算机行业的发展。

因为同样一个工具,总有人觉得不好用,觉得不好用,你拿出更好用的东西出来,大家都会学习你这种更加先进的理念。

正式因为有了这个不断循环往复的过程,才造就了近几十年以来,互联网行业的蓬勃发展。

深入剖析

说了太多对于这个话题的思考,还是让我们回到这个问题的本身来吧。

究竟是什么诱因,让我奋笔疾书的写下这篇文章的呢?

先来让我们检查一下 cnpm 的版本:

C:\Users\Administrator>cnpm --version
cnpm@6.1.0 (C:\Users\Administrator\AppData\Roaming\npm\node_modules\cnpm\lib\parse_argv.js)
npm@6.12.0 (C:\Users\Administrator\AppData\Roaming\npm\node_modules\cnpm\node_modules\npm\lib\npm.js)
node@10.16.3 (C:\Program Files\nodejs\node.exe)
npminstall@3.23.0 (C:\Users\Administrator\AppData\Roaming\npm\node_modules\cnpm\node_modules\npminstall\lib\index.js)
prefix=C:\Users\Administrator\AppData\Roaming\npm
win32 x64 10.0.18362
registry=https://r.npm.taobao.org

不得不说,这个命令显示的内容可真多,一下子将 nodejs、npm、cnpm 的版本都给暴露了,不过没关系,这正是我们想要看到的结果。

为了真实的情景再现,我接下来要安装 vue-cli 了:

C:\Users\Administrator>cnpm install vue-cli -g

为了文章的简介,安装过程的 log 就不黏贴上来了,反正没有报错,异常退出的话,vue-cli 就装成功了。

下面我来运行一下 vue :

C:\Users\Administrator>vue

C:\Users\Administrator>"node"  "C:\Users\Administrator\AppData\Roaming\npm\\node_modules\vue-cli\bin\vue"
Usage: vue <command> [options]

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  init           generate a new project from a template
  list           list available official templates
  build          prototype a new project
  create         (for v3 warning only)
  help [cmd]     display help for [cmd]

可以看到的是,我运行 vue 命令的时候,并没有直接执行 node vue 脚本 这样的命令,而是唤出了一串很字符来执行 vue:

"node"  "C:\Users\Administrator\AppData\Roaming\npm\\node_modules\vue-cli\bin\vue"

为什么会这样呢?

相信跟我以前一样,没怎么思考过这个问题的人,肯定会误以为,我们安装了某个模块,是不是说我们就安装了某个直接可以执行的二进制文件呢?

这个答案是否定的,其实我们安装的 nodejs 模块都是一些 nodejs 脚本,我们在调用像 vue 这样的命令的时候,其实就是调用 nodejs 这个引擎,去执行对应的 nodejs 脚本。

这个问题,你往大了想,就能够看透计算机的本质了。

我们计算机其实不能识别我们的编程语言,不说高级编程语言,即使是汇编、机器码他也无法识别,他最原始的一面是,只能识别 0、1 两个不同的电压信号。

机器码的作用,就是让我们来驱动不同的电压信号组合,来使计算机产生对应的反应。

所以简而言之,我们写代码,其实都只是在按照编程语言提供给我们的规则,来创造一些复杂的组合逻辑,做一些看似很简单的事情。

这个问题往深了说,就说到计算机组成原理、操作系统的本质等等方面了,我目前也只是略知一二,所以就不往这方面展开了。

我们只要明白,其实无论是我们全局安装的模块还是局部安装的模块,运行起来都是同一套逻辑。

甚至就连 npm 本身也就是一个模块,这个模块和其他的第三方模块也没有什么本质方面的区别。

image.png

打开全局安装的模块的目录,我们可以看到,有个 node_modules  文件夹,然后目录里面有我们全局安装的模块的命令行运行的脚本。

可以看到的是,与 vue 相关的脚本就有 3 个,这是因为我用 cnpm 安装的缘故,如果你用 npm 安装的,应该就只有两个脚本。

我们分别打开这三个脚本,看看里面的内容:

首先是 vue:

#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  "$basedir/node"  "$basedir/node_modules/vue-cli/bin/vue" "$@"
  ret=$?
else 
  node  "$basedir/node_modules/vue-cli/bin/vue" "$@"
  ret=$?
fi
exit $ret

可以看到,这是一个 bash 脚本,可以直接在 Linux 或者 Mac 下运行的。

其次是 vue.cmd:

@SETLOCAL

@IF EXIST "%~dp0\node.exe" (
  @SET "_prog=%~dp0\node.exe"
) ELSE (
  @SET "_prog=node"
  @SET PATHEXT=%PATHEXT:;.JS;=;%
)

"%_prog%"  "%~dp0\node_modules\vue-cli\bin\vue" %*
@ENDLOCAL

这是一个 Windows 批处理脚本,这个脚本也很简单,就是拿到 node 的路径,然后用 node 执行全局模块中的 vue。

最后是 vue.psl:

#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  & "$basedir/node$exe"  "$basedir/node_modules/vue-cli/bin/vue" $args
  $ret=$LASTEXITCODE
} else {
  & "node$exe"  "$basedir/node_modules/vue-cli/bin/vue" $args
  $ret=$LASTEXITCODE
}
exit $ret

这是一个 powerShell 脚本,同样也是拿到 node 的路径,然后执行 vue 脚本。

这个写法本身是没问题的,但是会造成在某些电脑上无法使用,比如我的电脑,执行的过程中,会报这样的错误:
image.png

接下来,让我们看看用 npm 安装的模块,生成的一键运行的脚本,有何不同呢?

为了公平起见,我们先将用 cnpm 安装的 vue-cli 删除掉:

E:\work2\caidademo>npm uninstall -g vue-cli

删除成功以后,再用 npm 安装一遍 vue-cli:

E:\work2\caidademo>npm install -g vue-cli

安装成功以后,我们会发现,这次只生成了两个脚本:

image.png

稍微想想就能明白,他们应该是分别运行在类 Unix 系统和 Windows 系统中的脚本。

vue 脚本我们就不看了,让我们来研究下 vue.cmd 脚本与之前的有何差异:

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\node_modules\vue-cli\bin\vue" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\node_modules\vue-cli\bin\vue" %*
)

可以看到,差异不大,唯一的差异就是,这个脚本里面直接用 node ,来执行 vue,不是用 "node.exe" ,因为这种写法,在 cmd 中是支持的,但是在 powerShell 中是不支持的。

所以其实我们也能用 cnpm 来管理我们的 node 模块,只是需要改一改 cnpm 给我们自动生成的脚本就行了。

又或许,这就是一个 bug,需要你给 npm 仓库去贡献代码,修改生成脚本的逻辑。

但是 cnpm 的问题肯定不仅仅只是这一个,这个问题只是其中的一个小问题而已。

所以,就像之前说的那样,如果可以的话,尽量用 npm 去管理 nodejs 模块吧。

对于某些曾经推动历史发展,后又淹没在历史的长河中的事务,我们同样保持敬意。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值