淺析有關執行npm run serve時發生了什麼的5個問題出發解析npm run的執行原理
一、npm run serve時發生了什麼?靈魂5問
1、npm run xxx 的時候,首先會去專案的 package.json 檔案裡找 scripts 裡對應的 xxx,然後執行 xxx 的命令,例如啟動vue專案 npm run serve 的時候,實際上就是執行了 package.json 檔案裡 scripts 下的 serve 對應的 vue-cli-service serve 這條命令
2、那為什麼不直接執行 vue-cli-service serve
而要執行 npm run serve
呢?
因為直接執行vue-cli-service serve
,會報錯,因為作業系統中沒有存在vue-cli-service
3、那既然vue-cli-service
這條指令不存在作業系統中,為什麼執行npm run serve
時,也就是相當於執行了vue-cli-service serve
,為什麼這樣它就能成功,而且不報指令不存在的錯誤呢?
我們重點關注下這個問題:為什麼執行npm run serve
時可以成功,而且不報指令不存在的錯誤呢?
因為我們在安裝依賴的時候,是通過 npm i xxx 來執行的,例如 npm i @vue/cli-service
,npm 在安裝這個依賴的時候,就會在 node_modules/.bin/
目錄中建立好 vue-cli-service
為名的幾個可執行檔案了。
.bin 目錄,這個目錄不是任何一個 npm 包目錄下的檔案,表示這是一個個軟連結,開啟檔案可以看到檔案頂部寫著 #!/bin/sh
,表示這是一個指令碼
#!/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
exec "$basedir/node" "$basedir/../@vue/cli-service/bin/vue-cli-service.js " "$@"
else
exec node "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"
fi
由此我們可以知道,當使用 npm run serve
執行 vue-cli-service serve
時,雖然沒有安裝 vue-cli-service
的全域性命令,但是 npm 會到 ./node_modules/.bin
中找到 vue-cli-service
檔案作為 指令碼來執行,則相當於執行了 ./node_modules/.bin/vue-cli-service serve
(最後的 serve 作為引數傳入)
4、.bin 目錄下的檔案表示軟連線,那這個bin目錄下的那些軟連線檔案是哪裡來的呢?它又是怎麼知道這條軟連線是執行哪裡的呢?
我們可以直接在新建的vue專案裡面搜尋vue-cli-service
可以看到,它存在專案的 package-lock.json 檔案中,從 package-lock.json 中可知,當我們npm i 整個新建的vue專案的時候,npm 將 bin/vue-cli-service.js 作為 bin 聲明瞭。
注意:bin欄位不是在自己專案的package.json檔案裡面,而是在庫的原始碼中的package.json裡面,用於在安裝時建立軟鏈指向bin中的地址
在 npm i 安裝時寫入到了專案的 package-lock.json 裡
所以在 npm install 時,npm 讀到該配置後,就將該檔案軟連結到 ./node_modules/.bin 目錄下,而 npm 還會自動把node_modules/.bin加入$PATH,這樣就可以直接作為命令執行依賴程式和開發依賴程式,不用全域性安裝了。
假如我們在安裝包時,使用 npm install -g xxx
來安裝,那麼會將其中的 bin 檔案加入到全域性,比如 create-react-app 和 vue-cli ,在全域性安裝後,就可以直接使用如 vue-cli projectName 這樣的命令來建立專案了。
也就是說,npm i 的時候,npm 就幫我們把這種軟連線配置好了,其實這種軟連線相當於一種對映,執行 npm run xxx 的時候,就會到 node_modules/bin 中找對應的對映檔案,然後再找到相應的js檔案來執行。
5、剛剛看到在node_modules/bin中有三個vue-cli-service檔案,為什麼會有三個檔案呢?
如果我們在 cmd 裡執行的時候,windows 一般是呼叫了 vue-cli-service.cmd
這個檔案,這是 windows 下的批處理指令碼。
所以當我們執行vue-cli-service serve
這條命令的時候,就相當於執行 node_modules/.bin/vue-cli-service.cmd serve
然後這個指令碼會使用 node 去執行 vue-cli-service.js
這個 js 檔案,由於 node 中可以使用一系列系統相關的 api ,所以在這個 js 中可以做很多事情,例如讀取並分析執行這條命令的目錄下的檔案,根據模板生成檔案等
# unix 系預設的可執行檔案,必須輸入完整檔名
vue-cli-service
# windows cmd 中預設的可執行檔案,當我們不新增字尾名時,自動根據 pathext 查詢檔案
vue-cli-service.cmd
# Windows PowerShell 中可執行檔案,可以跨平臺
vue-cli-service.ps1
6、總結:
(1)執行 npm run xxx的時候,npm 會先在當前目錄的 node_modules/.bin 查詢要執行的程式,如果找到則執行;
(2)沒有找到則從全域性的 node_modules/.bin 中查詢,npm i -g xxx就是安裝到到全域性目錄;
(3)如果全域性目錄還是沒找到,那麼就從 path 環境變數中查詢有沒有其他同名的可執行程式。
二、當輸入 npm run 後發生了什麼
在前端開發的工作當中,使用 npm run dev
的命令啟動本地開發環境,是再正常不過的事了。那麼,當輸入完類似 npm run xxx
的命令後,究竟是如何觸發各種構建工具的構建命令以及啟動 Node 服務等功能的呢?
首先我們知道,Node 作為 JavaScript 的執行時,可以把 .js
檔案當做指令碼來執行,像這種:node index.js
npm
來管理專案時,會在根目錄下生成一個 package.json
檔案,其中的 scripts
屬性,就是用於配置 npm run xxx
命令的,比如我有如下配置:
// package.json
{
// ...
"scripts": {
"start": "node ./src/index.js",
"build": "react-scripts build",
},
// ...
}
當執行 npm start
時,對於 npm
來說,相當於執行 npm run start
,則對映為 scripts
屬性下的 start
命令,即
npm start
# 相當於
npm run start
# 相當於
node ./src/index.js
這個比較好理解,就是直接使用全域性安裝的 Node 命令來執行了 ./src
目錄下的 index.js
檔案而已。
如上面類似,執行 npm run build
即相當於執行 react-scripts build
命令。這個命令,是使用 create-react-app
搭建 React 專案時預設配置的。與 Node 不同,react-scripts
並沒有全域性安裝,怎麼就能直接執行呢?
這時我們不妨看一下,使用 create-react-app
搭建的專案(使用 vue-cli
搭建的專案也一樣),在 npm install
後,其 node_modules
目錄下面的樣子:
如圖可以看到有一個 .bin
目錄,這個目錄不是任何一個 npm
包。目錄下的檔案,右面都有一個小箭頭(VS Code 上這樣顯示),表示這是一個軟連結,開啟檔案可以看到檔案頂部寫著 #!/user/bin/env node
,表示這是一個通過使用 Node 執行的指令碼。
npm run build
執行 react-scripts build
時,雖然沒有安裝 react-scripts
的全域性命令,但是 npm
會到 ./node_modules/.bin
中找到 react-scripts.js
檔案作為 Node 指令碼來執行,則相當於執行了 ./node_modules/.bin/react-scripts build
(最後的 build
作為引數傳入)。
npm run build
# 相當於
./node_modules/.bin/react-scripts build
前面說過,react-scripts
是一個軟連結,那麼它的指向是哪裡,又是怎麼來的呢?
我們可以在 node_modules
目錄下,直接找到 react-scripts
包,檢視其目錄結構和 package.json
如下:
從 package.json
中可知,這個包將 ./bin/react-scripts.js
作為 bin
聲明瞭。所以在 npm install
時,npm
讀到該配置後,就將該檔案軟連結到 ./node_modules/.bin
目錄下,而 npm
還會自動把node_modules/.bin加入$PATH,這樣就可以直接作為命令執行依賴程式和開發依賴程式,不用全域性安裝了。
假如我們在安裝包時,使用 npm install -g xxx
來安裝,那麼會將其中的 bin
檔案加入到全域性,比如 create-react-app
和 vue-cli
,在全域性安裝後,就可以直接使用如 vue-cli projectName
這樣的命令來建立專案了。
第二節來源於:https://juejin.cn/post/6971723285138505765
還有這篇文章,可以學一下:https://juejin.cn/post/7025908145650139144