1. 程式人生 > 其它 >淺析有關執行npm run serve時發生了什麼的5個問題出發解析npm run的執行原理

淺析有關執行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-appvue-cli ,在全域性安裝後,就可以直接使用如 vue-cli projectName 這樣的命令來建立專案了。

  第二節來源於:https://juejin.cn/post/6971723285138505765

  還有這篇文章,可以學一下:https://juejin.cn/post/7025908145650139144