記錄macOS下由yarn與npm差異引發的Electron映象地址讀取問題
寫在前面:該問題僅僅出現在Linux和macOS上,Windows上不存在該問題!
最近筆者重新拾起了Electron,把最新版Electron的官方檔案閱讀了一遍。眾所周知,Electron作為依賴在安裝的時候,其二進位制檔案下載在國內一直以來都是問題(因為預設會從github上下載),好在現在Electron的官方檔案已經寫的非常詳細了:安裝指導 | Electron (electronjs.org),只需要設定一個映象地址到.npmrc
中:
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
記住這個大寫的Key
筆者由於是新的機器,還沒有設定改值,所以找到.npmrc
檔案的設定了上述的映象後,便開開心心的準備進行專案搭建了。
然而,當筆者準備使用yarn執行如下命令的時候,卻出了問題:
yarn add -D electron
執行啟動以後,在Electron安裝的環境一直卡住了很久很久。
咦,難道映象設定寫錯了嗎?仔細對比以後,沒有問題。難道因為我的網路存取很慢嗎?等到存取超時以後,發現一個IP地址超時了,心想國內映象再怎麼也不應該超時,盲猜映象地址沒有生效。於是乎,準備嘗試對下載Electron二進位制檔案的過程進行debug。
首先定位到node_module/electron
包,能夠看到有一段安裝後指令碼執行命令(postinstall
):
關於postinstall的詳細說明:scripts | npm Docs (npmjs.com)
也就是說,node_module/electron
本身npm包install完成以後,還會執行其包內的install.js。
定位進入了node_module/electron
包下的install.js
,該指令碼內部主要邏輯是先檢查Electron的二進位制快取,如果不存在快取,則使用來自@electron/get
包中提供的downloadArtifact
方法從遠端下載Electron二進位制製品檔案。
我們暫時先不看快取讀寫的邏輯,著重瞭解遠端下載的邏輯,所以我們進入@electron/get
包中的downloadArtifact
:
檢視@electron/get
包下的index.js內容:
前面我們提到,懷疑映象地址沒有生效導致下載超時,所以我們重點關注一下這裡通過getArtifactRemoteURL
方法得到的url
值,
由於每一次這個包都會重新安裝,我們不太好偵錯這個值,所以,我們做一個簡單的trick:
~/Library/Caches/Yarn/v6/npm-@electron-get-xxxx
):yarn add -D electron
。執行以後,等到超時以後,發現控制檯紀錄檔列印如下:Why!?為什麼這個下載的Electron二進位制檔案地址依然是github的?於是,我們有必要進一步檢視這個URL是如何得到。
繼續檢視程式碼,這個url
來源於artifact-utils
中的getArtifactRemoteURL
方法,而這個方法裡面關於最終返回的url
最重要的部分是下圖所示的base
的值:
而這個base
值來源於mirrorVar
這個方法:
根據上面程式碼的邏輯,name值為"mirror"
,options未使用,defaultValue為:
"https://github.com/electron/electron/releases/download/"
也就是說,在後面的邏輯中,如果沒有從process.env
中找到對應的值,那麼就會使用預設的github官方製品地址的值。按照程式碼邏輯,執行到這個方法的時候,會從process.env
中嘗試獲取:
環境變數—— 設定 | npm 中文網 (nodejs.cn)
任何以
npm_config_
開頭的環境變數都將被解釋為設定引數。 例如,將npm_config_foo=bar
放入您的環境中會將foo
設定引數設定為bar
。 任何未賦值的環境設定都將被賦值為true
。 設定值不區分大小寫,因此NPM_CONFIG_FOO=bar
的工作方式相同。 但是,請注意,在scripts
內部,npm 將設定自己的環境變數,並且 Node 會更喜歡那些小寫版本,而不是您可能設定的任何大寫版本。 詳情見此問題。請注意,您需要使用下劃線而不是破折號,因此
--allow-same-version
將變為npm_config_allow_same_version=true
。此外,如果是設定在npmrc裡面的設定,也會在npm/yarn啟動的時候被作為環境變數放到process.env中被存取。
那我們在.npmrc
中設定的ELECTRON_MIRROR
,在process.env
中變成了什麼呢?通過新增紀錄檔列印,我們會看到:
可以看到,在process.env
中,這個鍵為"npm_config_ELECTRON_MIRROR"
(npm_config
小寫,ELECTORN_MIRROR
大寫)。我們知道,nodejs中object物件的屬性值是大小寫敏感的!所以,當上面的mirrorVar
程式碼執行,嘗試獲取process.env
中的值的時候,根本找不到了,因為沒有"NPM_CONFIG_ELECTRON_MIRROR"
、"npm_config_electron_mirror"
、"npm_package_config_electron_mirror"
、"ELECTRON_MIRROR"
這些屬性。
然而,如果我們使用npm進行安裝的時候:
npm install -D electron
又能夠很快安裝。Why?!難道npm和yarn下的執行環境有差異嗎?為了驗證,我們編寫一個簡單的index.js程式碼:
console.log("process.env['npm_config_electron_mirror']", process.env['npm_config_electron_mirror']);
console.log("process.env['NPM_CONFIG_ELECTRON_MIRROR']", process.env['NPM_CONFIG_ELECTRON_MIRROR']);
console.log("process.env['npm_config_ELECTRON_MIRROR']", process.env['npm_config_ELECTRON_MIRROR']);
然後,在package.json中新增指令碼:
{
"name": "simple-electron-main-app",
"version": "1.0.0",
"scripts": {
+ "start": "node index.js"
},
"devDependencies": {}
}
最後,我們分別使用yarn(yarn start
)和npm(npm run start
)來執行指令碼:
在yarn執行上下文中,.npmrc
中的"ELECTRON_MIRROR"
直接拼接到了"npm_config_"
後邊,作為process.env
的一個屬性,所以你只能存取process.env["npm_config_ELECTRON_MIRROR"]
得到值;
在npm執行山下文中,.npmrc
中的"ELECTRON_MIRROR"
首先被轉為了小寫,然後拼接到了"npm_config_"
後邊,作為了process.env
的屬性,所以你需要存取process.env["npm_config_electron_mirror"]
來得到值。
終於,我們能解釋為什麼當我們在.npmrc
設定大寫的ELECTRON_MIRROR
的時候,使用yarn add -D electron
安裝electron的時候,二進位制映象地址沒有生效了。那麼,解決的辦法也非常簡單,兩種:
.npmrc
設定改為小寫key:electron_mirror="https://npmmirror.com/mirrors/electron/"
;npm
上下文環境進行安裝。個人更加建議按照第一種方式設定,不然大小寫敏感的坑太容易發生了。
process.env | Node.js API 檔案 (nodejs.cn)
在 Windows 作業系統上,環境變數不區分大小寫。
const { env } = require('node:process');
env.TEST = 1;
console.log(env.test);
// => 1
也就是說,在Windows機器上,即使process.env
中的key為"npm_config_ELECTRON_MIRROR"
,你也可以通過"npm_config_electron_mirror"
或者是"NPM_CONFIG_ELECTRON_MIRROR"
來存取這個值: