起因
因為工作的時候勾扭,公司提供的是 Windows 臺式機纽绍,因此一般都是在 Windows 環(huán)境下開發(fā)的。
但是最近在用 cnpm 安裝腳本的時候上鞠,忽然發(fā)現(xiàn)一個很有意思的問題:用 cnpm 安裝的全局腳本际邻,比如 vue-cli 居然只能在 cmd 中運行,無法在 powerShell 中運行芍阎。
類似的問題世曾,其實以前也碰到過,但是以前一般都是報著能用就用谴咸,不去深究原理的想法轮听。
其實很多時候,自己內(nèi)心深處還是有一個聲音不斷地在提醒自己:“知其然更知其所以然方能走的更遠”岭佳!
但是奈何血巍,自己實力不允許,對很多東西都是一知半解珊随,沒有形成一套體系述寡,沒有擁有看透問題本質(zhì)的能力。
但是現(xiàn)在叶洞,開始找到感覺了鲫凶,于是現(xiàn)在碰到問題,多花點時間衩辟,稍微深究一下螟炫,事后收獲還是很大的。
閑話不多說艺晴,轉(zhuǎn)回我們的正題吧昼钻。
npm、cnpm封寞、yarn 選擇問題
至于為什么會用 npm然评,我想前端的同學應該都深有感觸,特別是國內(nèi)的前端同學狈究。
我想或多或少都會經(jīng)歷過一下場景:
同事1:反正大家都說 npm 不好用沾瓦!
同事2:npm 垃圾玩意兒,下東西太慢了谦炒!
同事3:cnpm 下東西快贯莺,比 npm 強一萬倍!
同事4:你竟然還在用 npm宁改,太老土了缕探!
。还蹲。爹耗。
類似的情景對話耙考,我想大家應該都經(jīng)歷過。
但是究竟為什么 npm 不好用潭兽,cnpm 好用呢倦始?
這種踩 npm 的情況,是從什么時候開始的山卦,現(xiàn)在還是這樣嗎鞋邑?
npm 跟 cnpm 的差別是什么呢?
npm 可以通過什么方式變得跟 cnpm 一樣好用嗎账蓉?
其實這些問題枚碗,并沒有多少人去深究,特別是對于很多其前端初級選手來說铸本,估計很多人都下意識的認為肮雨,其實這兩個是一個東西,都是下 nodejs 模塊的嘛箱玷。
估計很多人都這樣想:這些問題關(guān)我鳥事怨规,我研究的那么透徹,老板也不會給我漲工資拔恪波丰!我前端,寫好頁面就行了舱污,這些個框架呀舔,能用好就行了弥虐,我開車扩灯,干嘛要了解汽車的原理呢!
如果你符合以上的思維霜瘪,那么請及時中斷往下看的念頭吧珠插,這篇文章不適合你。
那么究竟為什么大家都重口一詞的說 npm 不好用 cnpm 好用呢颖对?
原因是就是捻撑,以前 npm 真的挺不好用的!
這篇博主的文章分析的非常好缤底,有興趣的同學可以閱讀一下:https://blog.xgheaven.com/2018/05/03/npm-to-yarn-to-npm/
就因為 npm 不好用顾患,所以才催生出像 cnpm、yarn 等第三方包管理系統(tǒng)个唧。
但是隨著 npm 6.x 版本的發(fā)布江解,這些問題已經(jīng)被被解決了,已經(jīng)被掃進了歷史的垃圾堆里了徙歼。
所以結(jié)論就是:現(xiàn)在用 nodejs 的包管理系統(tǒng)犁河,首選 npm鳖枕,實在不能用 npm 的情況下,再考慮用第三方的包管理系統(tǒng)桨螺。
比如 create-react-app 這個腳手架宾符,就是只支持 yarn 的,但是如果你非要用 npm灭翔,就很麻煩了魏烫。
但是有的童鞋會發(fā)問,npm 安裝模塊太慢了缠局,不能忍则奥。
說實話,我也忍不了狭园,忍不了你就改個 npm 模塊的源唄读处,從鏡像站去下載模塊不久快很多么。
這個問題其實不止是出現(xiàn)在 npm 上唱矛,很多包管理系統(tǒng)都會出現(xiàn)在這樣的問題罚舱。
比如 python 的 pip,Ubuntu 的 apt-get绎谦,homebrew管闷,centos 的 yum 等等,都會因為官方源服務器在國外窃肠,訪問起來太慢包个。
但是這個問題其實是有解決方案的,換成國內(nèi)的鏡像源就能解決冤留。
比如 npm 換成淘寶的國內(nèi)鏡像源就能解決下載過慢的問題了碧囊,下面是配置方式:
# 換源
npm config set registry https://registry.npm.taobao.org
# 檢查是否改成功了
npm config get registry
第三方包管理工具
再聊聊為什么會出現(xiàn)第三方包管理工具。
其實之前就說過了纤怒,因為 npm 在開始的時候不好用糯而,所以后來社區(qū)就誕生出了更優(yōu)秀的包管理工具。
但是 npm 也是在進步的泊窘,我們不能總是以一種陳舊的眼光去看待問題熄驼,以一種擁抱未來的姿態(tài)去剔除我們思想中的偏見成分。
npm 的這種歷史烘豹,有點類似于 JavaScript 的發(fā)展歷史瓜贾。
以前 JavaScript 很多地方用起來不友好,所以催生出了很多優(yōu)秀的 JavaScript 框架携悯,十年前祭芦,風頭最盛的大概就是 jQuery 了吧。
甚至一度蚌卤,有人豪言壯語的宣稱:不用學 JavaScript 了实束,學了 jQuery 就行了奥秆。
但是歷史總是這么驚人的相似,隨著 es6 以及后續(xù)版本的出現(xiàn)咸灿,jQuery构订、lodash 等很多增強 JavaScript 語言功能的框架都漸漸的開始退出了歷史的舞臺了。
至于原因避矢,我想大家因該也知道悼瘾,同樣實現(xiàn)一種功能,自帶的肯定是更好用的审胸,如果不好用亥宿,只能說明他還有提升的空間。
這個定律砂沛,在很多時候都是比較符合現(xiàn)實的表現(xiàn)的烫扼。
所以,為什么說代碼開源碍庵,有助于計算機行業(yè)的發(fā)展映企。
因為同樣一個工具,總有人覺得不好用静浴,覺得不好用堰氓,你拿出更好用的東西出來,大家都會學習你這種更加先進的理念苹享。
正式因為有了這個不斷循環(huán)往復的過程双絮,才造就了近幾十年以來,互聯(lián)網(wǎng)行業(yè)的蓬勃發(fā)展得问。
深入剖析
說了太多對于這個話題的思考囤攀,還是讓我們回到這個問題的本身來吧。
究竟是什么誘因椭赋,讓我奮筆疾書的寫下這篇文章的呢抚岗?
先來讓我們檢查一下 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
不得不說或杠,這個命令顯示的內(nèi)容可真多哪怔,一下子將 nodejs、npm向抢、cnpm 的版本都給暴露了认境,不過沒關(guān)系,這正是我們想要看到的結(jié)果挟鸠。
為了真實的情景再現(xiàn)叉信,我接下來要安裝 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
命令的時候营袜,并沒有直接執(zhí)行 node vue 腳本
這樣的命令,而是喚出了一串很字符來執(zhí)行 vue:
"node" "C:\Users\Administrator\AppData\Roaming\npm\\node_modules\vue-cli\bin\vue"
為什么會這樣呢丑罪?
相信跟我以前一樣荚板,沒怎么思考過這個問題的人,肯定會誤以為吩屹,我們安裝了某個模塊跪另,是不是說我們就安裝了某個直接可以執(zhí)行的二進制文件呢?
這個答案是否定的煤搜,其實我們安裝的 nodejs 模塊都是一些 nodejs 腳本免绿,我們在調(diào)用像 vue 這樣的命令的時候,其實就是調(diào)用 nodejs 這個引擎擦盾,去執(zhí)行對應的 nodejs 腳本针姿。
這個問題,你往大了想厌衙,就能夠看透計算機的本質(zhì)了距淫。
我們計算機其實不能識別我們的編程語言,不說高級編程語言婶希,即使是匯編榕暇、機器碼他也無法識別,他最原始的一面是喻杈,只能識別 0彤枢、1 兩個不同的電壓信號。
機器碼的作用筒饰,就是讓我們來驅(qū)動不同的電壓信號組合缴啡,來使計算機產(chǎn)生對應的反應。
所以簡而言之瓷们,我們寫代碼业栅,其實都只是在按照編程語言提供給我們的規(guī)則,來創(chuàng)造一些復雜的組合邏輯谬晕,做一些看似很簡單的事情碘裕。
這個問題往深了說,就說到計算機組成原理攒钳、操作系統(tǒng)的本質(zhì)等等方面了辽社,我目前也只是略知一二濒翻,所以就不往這方面展開了恭垦。
我們只要明白,其實無論是我們?nèi)职惭b的模塊還是局部安裝的模塊晤斩,運行起來都是同一套邏輯。
甚至就連 npm 本身也就是一個模塊姆坚,這個模塊和其他的第三方模塊也沒有什么本質(zhì)方面的區(qū)別尸昧。

打開全局安裝的模塊的目錄,我們可以看到旷偿,有個 node_modules
文件夾烹俗,然后目錄里面有我們?nèi)职惭b的模塊的命令行運行的腳本。
可以看到的是萍程,與 vue 相關(guān)的腳本就有 3 個幢妄,這是因為我用 cnpm 安裝的緣故,如果你用 npm 安裝的茫负,應該就只有兩個腳本蕉鸳。
我們分別打開這三個腳本,看看里面的內(nèi)容:
首先是 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 執(zhí)行全局模塊中的 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 的路徑,然后執(zhí)行 vue 腳本咽弦。
這個寫法本身是沒問題的徒蟆,但是會造成在某些電腦上無法使用,比如我的電腦型型,執(zhí)行的過程中段审,會報這樣的錯誤:

接下來,讓我們看看用 npm 安裝的模塊闹蒜,生成的一鍵運行的腳本寺枉,有何不同呢?
為了公平起見嫂用,我們先將用 cnpm 安裝的 vue-cli 刪除掉:
E:\work2\caidademo>npm uninstall -g vue-cli
刪除成功以后型凳,再用 npm 安裝一遍 vue-cli:
E:\work2\caidademo>npm install -g vue-cli
安裝成功以后丈冬,我們會發(fā)現(xiàn)嘱函,這次只生成了兩個腳本:

稍微想想就能明白,他們應該是分別運行在類 Unix 系統(tǒng)和 Windows 系統(tǒng)中的腳本埂蕊。
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
槐脏,來執(zhí)行 vue,不是用 "node.exe"
撇寞,因為這種寫法顿天,在 cmd 中是支持的,但是在 powerShell 中是不支持的蔑担。
所以其實我們也能用 cnpm 來管理我們的 node 模塊牌废,只是需要改一改 cnpm 給我們自動生成的腳本就行了。
又或許啤握,這就是一個 bug鸟缕,需要你給 npm 倉庫去貢獻代碼,修改生成腳本的邏輯排抬。
但是 cnpm 的問題肯定不僅僅只是這一個懂从,這個問題只是其中的一個小問題而已。
所以蹲蒲,就像之前說的那樣番甩,如果可以的話,盡量用 npm 去管理 nodejs 模塊吧届搁。
對于某些曾經(jīng)推動歷史發(fā)展对室,后又淹沒在歷史的長河中的事務,我們同樣保持敬意咖祭。