這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 npm 是 node 捆綁的依賴管理器,常用程度可想而知。那麼你每天都在 npm/yarn run 的命令到底是如何運行項目的呢? 前端項目中運行 npm run xxx 的時候發生了什麼?大家都知道目前的 node 是捆綁 npm 的。 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
npm 是 node 捆綁的依賴管理器,常用程度可想而知。那麼你每天都在 npm/yarn run 的命令到底是如何運行項目的呢?
前端項目中運行 npm run xxx 的時候發生了什麼?
大家都知道目前的 node 是捆綁 npm 的。npm 是 node 的依賴管理器,雖然它不是唯一的選擇,我們還有 pnpm/yarn/cnpm/ni 。
但是,的依賴管理器都是在解決 npm 的某個痛點。對於 npm 依賴聲明文件
package.json
本身是基本沒有變化的。
例如我們可以使用
npm run serve
運行某個命令, 也可以使用
yarn serve
運行某個命令。
可以看到在這個地方 yarn 可以省略 run 這個參數。
但是,他們都只是對
package.json
進行解析而已,例如下麵的文件,當運行
npm run serve
時,其實就是運行該 json 文件中的
scripts
下的
serve
鍵對應的命令。
{ "name": "h5", "version": "1.0.7", "private": true, "scripts": { "serve": "vue-cli-service serve" }, "dependencies": { "axios": "^0.19.2", "vuex": "^3.4.0" }, "devDependencies": { "node-sass": "^4.12.0" } }
上面說是 命令
只是用於方便理解,例如:
npm run server # 類似於在命令行運行以下命令 vue-cli-service serve
通過 npm run 與直接運行命令的區別
還是用上面的配置來描述:
{ "scripts": { "serve": "vue-cli-service serve" } }
npm 在運行
vue-cli-service serve
這條命令的時候,會先在當前
node_modules/.bin
下麵看有沒有同名的可執行文件,如果有,則使用其運行。
這裡我們可以打開這個目錄看看:
如果直接在命令行中運行
vue-cli-service serve
這條命令,是不會從 node_modules 中查找可執行程式的。
運行可執行文件
那麼什麼叫可執行文件呢?上面的圖中有很多個同名的 vue-cli-service ,到底是運行哪個?
我們先來分析這幾個文件怎麼來的?
例如
@vue/cli-service
有以下
package.json
文件,註意 bin 欄位,當我們運行
npm i @vue/cli-service
這條命令時,npm 就會在
node_modules/.bin/
目錄中創建好以
vue-cli-service
為名的幾個可執行文件了。
{ "name": "@vue/cli-service", "version": "4.4.6", "description": "local service for vue-cli projects", "main": "lib/Service.js", "typings": "types/index.d.ts", "bin": { "vue-cli-service": "bin/vue-cli-service.js" } }
對於可執行這個定義,每個系統不一樣。在 windows 系統上,可執行文件是通過組策略和環境變數決定的。
使用
set pathext
可以查看
pathext
這個環境變數,他定義了可以作為可執行文件的尾碼。
# 查看可執行文件尾碼 set pathext
由上面的配置可以發現,我們常見的 exe 也在其中,這個可執行文件在 windows 上,在命令行中輸入文件名或雙擊時即可以運行。
在 unix 系統上面,是通過設置文件的屬性為可執行,再在文件中的第一行聲明解釋器來運行的。
如果我們在 cmd 里運行的時候,windows 一般是調用了
vue-cli-service.cmd
這個文件,這是 windows 下的批處理腳本:
@ECHO off SETLOCAL CALL :find_dp0 SET _maybeQuote=" IF EXIST "%dp0%\node.exe" ( SET "_prog=%dp0%\node.exe" ) ELSE ( SET _maybeQuote= SET "_prog=node" SET PATHEXT=%PATHEXT:;.JS;=;% ) %_maybeQuote%%_prog%%_maybeQuote% "%dp0%\..\_@[email protected]@@vue\cli-service\bin\vue-cli-service.js" %* ENDLOCAL EXIT /b %errorlevel% :find_dp0 SET dp0=%~dp0 EXIT /b
所以當我們運行
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
這裡多提了下,在 windows 中 cmd 腳本使用得比較多,相容性也較好。 powerShell 雖然比較強大,但他運行命令的方式由於和 cmd 命令有較大不同,這會導致你常常搞不清什麼命令應該在什麼解釋器里運行。
示例:運行命令的方式不相容
示例:windows 很多系統會預設禁止此腳本運行,導致 npm 命令運行錯誤
所以如果遇到 powerShell 相關錯誤時建議用 cmd 試試。
註入相關運行時信息
這一節我們通過調試 npm 的源碼來進行說明。
首先我們在 mockm 這個前端介面聯調工具的源碼中先來個 debugger, 註意有從 process.env 中獲取 NPM_CONFIG_REGISTRY 這個環境變數,這是 npm 安裝時可配置的鏡像地址。
然後我們再看一下這個環境變數,在當前系統中是沒有定義的。
讓我們開始調試 mockm package.json 中的 scripts
npm run s2
{ "scripts": { "s2": "node run.js remote --config=D:/git2/mockm/server/example/full.mm.config.js", } }
為了節省篇幅,這裡直接斷點在關鍵地點:
這是 [email protected] 的源碼,可以發現 npm 使用了 npm-lifecycle 這個依賴來運行的子進程調用我們的
run.js
文件。
在通過 spawn 運行 run.js 的時候,同時設置了進程相關的一些信息,這是由 node 原生支持的。
例如剛剛說到的 NPM_CONFIG_REGISTRY 環境變數。
下麵把繼續進入下一個斷點, run.js 文件:
可以發現子進程成功獲取了父進程給予的信息。
總結
運行 npm run xxx
的時候,npm 會先在當前目錄的 node_modules/.bin 查找要執行的程式,如果找到則運行;
沒有找到則從全局的 node_modules/.bin 中查找,npm i -g xxx
就是安裝到到全局目錄;
如果全局目錄還是沒找到,那麼就從 path 環境變數中查找有沒有其他同名的可執行程式。