1. 程式人生 > 其它 >三面面試官:執行 npm run xxx 的時候發生了什麼?

三面面試官:執行 npm run xxx 的時候發生了什麼?

事情是這樣的,直接開講

面試官:npm run xxx的時候,發生了什麼?講的越詳細越好。

我(心想,簡單啊): 首先,DNS 解析,將域名解析成 IP 地址,然後
TCP 連線,TCP 三次握手...

面試官:停停,我問的不是從URL輸入到頁面展現到底發生什麼?,是npm run xxx的時候,發生了什麼。

我(尷尬,條件反射地以為是問的八股文):emmmm,我記得 npm run xxx的時候,首先會去專案的package.json檔案裡找scripts 裡找對應的xxx,然後執行 xxx的命令,例如啟動vue專案 npm run serve的時候,實際上就是執行了vue-cli-service serve 這條命令。(好險,幸好這點常識我還是懂的)

package.json檔案

{
  "name": "h5",
  "version": "1.0.7",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve"
   },
}

面試官:嗯,不錯,那 為什麼 不直接執行vue-cli-service serve而要執行npm run serve 呢?

我(支支吾吾):emm,因為 npm run serve 比較簡短,比較好寫。

面試官:你再想想。

我(啊?不對嗎,對哦,我想起來了): 因為 直接執行vue-cli-service serve,會報錯,因為作業系統中沒有存在vue-cli-service

這一條指令

面試官: 哦,對對對,不錯不錯,喲西喲西!

我(嘿嘿,穩了,這次我要30k): 嘻嘻!

面試官:那既然vue-cli-service這條指令不存在作業系統中,為什麼執行npm run serve的時候,也就是相當於執行了vue-cli-service serve ,為什麼這樣它就能成功,而且不報指令不存在的錯誤呢?

我(啊?要不你還是把我鯊了吧,不想再勉強作回答):不好意思,這個我還沒了解過。

面試官:emmm,好吧,沒關係,我們做下一道演算法題吧:....

....

後面無關此次文章的內容,就省略過了。

面試官:好的,此處面試到此結束,我們會在一週內回覆您的面試結果

嗶嗶嗶...(電話結束通話)

唉。看來是涼了

為什麼執行npm run serve的時候,這樣它就能成功,而且不報指令不存在的錯誤呢?

我趕緊問問了大佬朋友這一過程到底是發生了什麼

經過一番討論,終於找到了答案。

不服輸的我,趕緊回撥了面試官的電話號碼。

我:喂,面試官,您好,我已經找到答案了,可以麻煩您再聽一下嗎?

面試官:嗯,可以啊,請講。

我:我們在安裝依賴的時候,是通過npm i xxx 來執行的,例如 npm i @vue/cli-service,npm 在 安裝這個依賴的時候,就會node_modules/.bin/ 目錄中建立 好vue-cli-service 為名的幾個可執行檔案了。

.bin 目錄,這個目錄不是任何一個 npm 包。目錄下的檔案,表示這是一個個軟連結,開啟檔案可以看到檔案頂部寫著 #!/bin/sh ,表示這是一個指令碼。

由此我們可以知道,當使用 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 作為引數傳入)。

面試官:可以啊,真不錯,但是我還想繼續問問,你說.bin 目錄下的檔案表示軟連線,那這個bin目錄下的那些軟連線檔案是哪裡來的呢?它又是怎麼知道這條軟連線是執行哪裡的呢?

我(竊喜,這個我們剛剛也討論了):我們可以直接在新建的vue專案裡面搜尋vue-cli-service

可以看到,它存在專案最外層的package-lock.json檔案中

從 package-lock.json 中可知,當我們npm i 整個新建的vue專案的時候,npm 將 bin/vue-cli-service.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 這樣的命令來建立專案了。

面試官:搜噶,也就是說,npm i 的時候,npm 就幫我們把這種軟連線配置好了,其實這種軟連線相當於一種對映,執行npm run xxx 的時候,就會到 node_modules/bin中找對應的對映檔案,然後再找到相應的js檔案來執行。

我(瘋狂點頭):嗯嗯,是的,就是這樣

面試官:我有點好奇。剛剛看到在node_modules/bin中 有三個vue-cli-service檔案。為什麼會有三個檔案呢?

我:如果我們在 cmd 裡執行的時候,windows 一般是呼叫了 vue-cli-service.cmd,這個檔案,這是 windows 下的批處理指令碼:

@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

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

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\@vue\cli-service\bin\vue-cli-service.js" %*

所以當我們執行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

面試官:原來如此,不錯嘛小夥子,短短時間內就掌握清楚了,看來學習能力很強,不錯不錯,我很看好你,我會催hr儘快回覆你的。先這樣了,拜拜

我(欣喜若狂,功夫不負有心人啊):好啊,好啊,拜拜

嗶嗶嗶...(電話結束通話)

過了三十分鐘....

今天是個好日子,心想的事兒都能成,今天是個好日子,打開了家門咱迎春風...(手機鈴聲響起)。

我:喂,您好。

hr:您好,我是xxx公司的hr,根據你面試的優秀表現,恭喜你獲得了我司的offer,經過我最大的努力,我給你爭取到了最大的薪資,薪資是月薪3500,您看滿意嗎?

我:....

嗶嗶嗶....(電話結束通話)

tmd,c

總結

  1. 執行 npm run xxx的時候,npm 會先在當前目錄的 node_modules/.bin 查詢要執行的程式,如果找到則執行;
  2. 沒有找到則從全域性的 node_modules/.bin 中查詢,npm i -g xxx就是安裝到到全域性目錄;
  3. 如果全域性目錄還是沒找到,那麼就從 path 環境變數中查詢有沒有其他同名的可執行程式。

參考文章

https://blog.51cto.com/u_15077533/4531157
https://juejin.cn/post/6971723285138505765