Electron安裝過程深刻解析(讀完此文解決Electron安裝失敗致使的沒法啓動,沒法打包的問題)

1. 安裝Electron依賴包

開發者每每經過npm install(或 yarn add)指令完成爲Node.js工程安裝依賴包的工做,html

安裝Electron也不例外,下面是npmyarn的安裝Electron依賴包的指令:node

npm install electron --save-dev
yarn add electron --dev

官方推薦咱們把electron依賴包安裝爲開發依賴(devDependencies),linux

這其實是爲了未來製做應用程序安裝包時,git

避免把electron包和其可執行文件包裝兩次。這部份內容後文咱們會詳細講解。github

若是你但願觀察npm install指令的具體執行細節,能夠爲其添加兩個參數,以下所示:shell

npm install electron --save-dev  --timing=true --loglevel=verbose

經過以上指令安裝Electron依賴包,你會觀察到整個安裝過程的執行狀況日誌,npm

這裏我截取幾個重要的日誌分析一下。編程

npm http fetch GET 200 https://registry.npm.taobao.org/electron 125ms

這是npm經過http協議獲取electron包的註冊信息的日誌,json

請求上述地址(我用的是npm淘寶源)將獲得一個json響應,windows

json中包含了electron的全部版本的版本信息,

若是安裝時咱們沒有爲electron指定版本號,將安裝最新的版本。

> electron@9.2.0 postinstall D:\ElectronDeepDive\capture1\install\node_modules\electron

> node install.js

這是Electron依賴包安裝完成後,npm執行其依賴包內定義的postinstall鉤子的日誌。

npm包管理文檔爲npm包定義了一系列的鉤子,postinstall鉤子會在npm包安裝完成後被執行,

除了postinstall鉤子以外,經常使用的還有以下這些鉤子:

l preinstall包安裝以前執行;

l postuninstall包被卸載以後執行;

l preuninstall包被卸載以前執行;

l poststartnpm start執行後觸發;

l poststopnpm stop執行後觸發;

l posttestnpm test執行後觸發;

詳細的文檔請參閱:https://docs.npmjs.com/misc/scripts

postinstall鉤子定義在Electron包內的package.json中,代碼以下:

"scripts": {

  "postinstall": "node install.js"

}

install.js程序是Electron包內的一個重要程序,用於下載Electron的可執行文件及相關資源,下一小節咱們將講解Electron可執行文件的下載過程。

2. 下載Electron的二進制文件

install.js中,程序獲取了當前操做系統的版本,並經過以下代碼下載Electron的二進制文件與相應的資源:

downloadArtifact({

  version,

  artifactName: 'electron',

  force: process.env.force_no_cache === 'true',

  cacheRoot: process.env.electron_config_cache,

  platform: process.env.npm_config_platform || process.platform,

  arch: process.env.npm_config_arch || process.arch

}).then(extractFile).catch(err => {

  console.error(err.stack)

  process.exit(1)

})

downloadArtifact方法是@electron/get包提供的,這個包是Electron包依賴的一個npm包,因爲自npm 3.x以來,npm把包管理方式從嵌套結構切換到了扁平結構,因此@electron/get位於當前工程的node_modules目錄的根目錄下。

拓展:在npm 3.x之前,npm的包管理方式是嵌套結構的,也就是說一個工程安裝的依賴包位於當前工程根目錄下的node_modules目錄中,假設其中一個依賴包又依賴了其餘npm包,咱們假設這個依賴包叫作packageA,那麼它的依賴包會被安裝在packageA目錄的node_modules目錄下,以此類推。以這種方式管理依賴包會致使目錄層級很深,在Windows操做系統中,文件路徑最大長度爲260個字符,目錄層級過深會致使依賴包安裝不成功。而且不一樣層級的依賴中可能引用了同一個依賴包,這種結構也沒辦法複用這個依賴包,並且這種狀況很是常見,形成了大量的冗餘、浪費。

npm 3.x以來,npm的包管理方式升級爲了扁平解構,不管是當前工程的依賴包仍是依賴包的依賴包,都會被優先安裝到當前工程的node_modules目錄下,在安裝過程當中若是npm發現當前工程的node_modules目錄下已經存在了相同版本的某個依賴包,那麼就會跳過安裝過程,直接讓工程使用這個已安裝的依賴包,只有在版本不一樣的狀況下,纔會在這個包的node_modules目錄下安裝新的依賴包。這就很好的解決了前面兩個問題。但也引來了新的問題,直到npm 5.x引入了package lock的機制後,才解決了新的問題,這已超出了本書的討論範圍,詳情請參閱:https://docs.npmjs.com/configuring-npm/package-lock-json.html。另外,npm判斷兩個依賴包是否版本相同,是有一套複雜的規則的,這也超出了本書的討論範圍,詳情請參閱:https://docs.npmjs.com/about-semantic-versioning

downloadArtifact方法的入參是一個配置對象,

對象的force屬性標記着是否須要強制下載Electron的二進制文件,

若是環境變量force_no_cache的值爲"true"則不管本地有沒有緩存,都會從Electron的服務器下載相應的文件。

配置對象的version屬性是須要下載的Electron可執行程序的版本號,

這個版本號就是定義在Electron npm包的package.json內的版本號。

platform屬性是當前的操做系統的名稱,

可能的值爲"darwin""win32""linux"等,arch是你當前操做系統的架構,

可能的值爲"x32""x64",這些信息都是幫你肯定下載什麼版本的Electron可執行文件的。

上述信息最終被組裝成的下載地址多是以下的樣子(其中版本號視真實狀況而定):

https://github.com/electron/electron/releases/download/v9.2.0/electron-v9.2.0-win32-x64.zip

若是處於windows操做系統內,上述文件會被首先下載到以下目錄中:

C:\Users\ADMINI~1\AppData\Local\Temp

這個目錄是Node.js經過os.tmpdir()肯定的。文件下載完成後,

程序會把它複製到緩存目錄中以備下次使用,這個機制極大的節省了開發者的時間成本,

下一小節咱們將深刻講解Electron安裝過程當中的緩存和鏡像機制。

緩存完成後,上述代碼中的extractFile回調方法被執行,

此方法會把緩存目錄下的二進制文件壓縮包解壓到當前Electron依賴包的dist目錄下:

[project]\node_modules\electron\dist

除了下載Electron二進制文件的壓縮包外,downloadArtifact還單獨下載了一個SHASUMS256.txt文件,

這個文件內記錄了Electron二進制文件壓縮包的sha256值,

程序會對比一下這個值與壓縮包文件的sha256值是否匹配,以免用戶請求被截獲,

下載到不安全的文件的狀況(這方面的效用只能說聊勝於無),或者是下載過程意外終止,文件數據不完整的狀況。

3. 緩存與鏡像策略

上文中咱們提到Electron的二進制文件壓縮包下載成功後,會複製一份到緩存目錄,以備下次使用。在Windows環境下,默認的緩存目錄爲:

C:\Users\Administrator\AppData\Local\electron\Cache

這是經過Node.jsos.homedir()再附加了幾個子目錄肯定的。你能夠經過設置electron_config_cache環境變量來提供用戶自定義緩存目錄,在命令行下臨時設置這個環境變量的方式爲:

> set electron_config_cache=D:\ElectronDeepDive\capture1\cache

 若是你是經過編程的方式使用@electron/get包,那麼也能夠經過以下方式把環境變量的設置寫到代碼裏:

process.env.electron_config_cache="D:\\ElectronDeepDive\\capture1\\cache"

若是你但願一勞永逸的解決這個問題,還能夠把這個環境變量配置到操做系統中去,以下圖所示:

 

1-1 Electron緩存目錄環境變量設置

在國內網絡環境不理想的狀況下,安裝Electron npm包十有八九會失敗,

這就是Electron的二進制文件壓縮包難如下載成功緻使的,

知道了緩存目錄的位置以後,你就能夠先手動把Electron二進制包安放到相應的緩存目錄中,

這樣再安裝Electron npm包時就毫無阻滯了。

你能夠從同事的電腦上拷貝相應版本的Electron二進制包,

也能夠從淘寶的鏡像源手動下載Electron的二進制包,淘寶Electron鏡像源的地址爲:

https://npm.taobao.org/mirrors/electron/

下載好的壓縮包和哈希值文件必定要按照以下路徑放置在緩存目錄裏:

//二進制包文件的路徑

[你的緩存目錄]/httpsgithub.comelectronelectronreleasesdownloadv9.2.0electron-v9.2.0-win32-x64.zip/electron-v9.2.0-win32-x64.zip

//哈希值文件的路徑

[你的緩存目錄]/httpsgithub.comelectronelectronreleasesdownloadv9.2.0SHASUMS256.txt/SHASUMS256.txt

路徑中[你的緩存目錄]下的子目錄的命名方式看起來有些奇怪,

這其實就是下載地址格式化得來的(經過一個叫作sanitize-filename的工具庫,

去除了url路徑中的斜槓,使得其能成爲文件路徑),

在個人電腦上,這兩個路徑是以下形式:

 

 

 

1-2 二進制包文件的路徑

 

 

 

1-3 哈希值文件的路徑

細心的讀者可能已經注意到了,個人路徑並非githuburl地址格式化得來的,

而是taobao的鏡像源地址格式化得來的。下面咱們就介紹一下如何設置第三方鏡像源。

@electron/get庫下載Electron二進制文件包的地址被人爲的分割成了三部分:

  • 鏡像部分:https://github.com/electron/electron/releases/download/
  • 版本部分:v9.2.0/
  • 文件部分:electron-v9.2.0-win32-x64.zip

這三部分聯合起來最終構成了下載地址,每一個部分都有其默認值,也有對應的重寫該部分值的環境變量:

  • 鏡像部分的環境變量:ELECTRON_MIRROR
  • 版本部分的環境變量:ELECTRON_CUSTOM_DIR
  • 文件部分的環境變量:ELECTRON_CUSTOM_FILENAME

通常狀況下,咱們只須要設置鏡像部分的環境變量便可,

好比要設置淘寶的鏡像源,

只須要把ELECTRON_MIRROR的環境變量的值設置爲https://npm.taobao.org/mirrors/electron/便可,

設置方式與設置緩存目錄的環境變量方式相同,此處再也不贅述。

4. bin目錄下注入命令

Electron依賴包安裝完成後,

npm會自動爲其在node_modules/.bin路徑下注入命令文件,

不帶擴展名的electron文件是爲linuxmac準備的shell腳本,

electron.cmd是傳統的windows批處理腳本,

electron.ps1是運行在windows powershell下的腳本。

 命令文件中的腳本代碼很少,以electron.cmd爲例,咱們簡單解釋一下:

@ECHO off

SETLOCAL

CALL :find_dp0

IF EXIST "%dp0%\node.exe" (

  SET "_prog=%dp0%\node.exe"

) ELSE (

  SET "_prog=node"

  SET PATHEXT=%PATHEXT:;.JS;=;%

)

"%_prog%"  "%dp0%\..\electron\cli.js" %*

ENDLOCAL

EXIT /b %errorlevel%

:find_dp0

SET dp0=%~dp0

EXIT /b

其中~dp0指執行腳本的當前目錄,

SET是爲一個變量賦值,

%*是執行命令時輸入的參數,

整段命令腳本的意思是用node執行Electron包內的cli.js文件,

並把全部命令行參數一併傳遞過去。

關於windows批處理的更多細節請參閱(https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/windows-commands

細心的讀者會發現,npm並不會爲全部的依賴包注入命令文件,

並且即便注入了命令文件的包也不必定存在cli.js文件,

好比npm就沒有爲core-js包注入命令文件,卻爲Mocha注入了兩組命令文件,

Electron或者Mocha的獨特之處在於它們的package.json裏都有相似以下這樣的配置(Mochabin對象配置了兩個屬性,因此npm爲其生成了兩組指令文件):

"bin": {

  "electron": "cli.js"

}

npm之因此在node_modules/.bin路徑下添加命令文件,

是由於不少包的做者都但願本身的腳本能放置在用戶的環境變量裏。

npm爲這個需求提供了便利。npm在執行一段腳本前,

好比:npm run dev,會先自動新建一個命令行環境,而後把當前目錄的node_modules/.bin加入到系統環境變量中,

接着執行scripts配置節指定的腳本的內容,

執行完成後再把node_modules/.bin從系統環境變量中刪除。

因此當前目錄的node_modules/.bin子目錄裏面的全部腳本,

均可以直接用腳本名調用,而沒必要加上路徑。

固然你的項目的package.json裏要配置了dev對應的指令,示例的配置代碼以下:

"scripts": { "dev": "electron ./index.js" }

有了上面的配置,你就能夠經過運行npm run dev命令來啓動你安裝過的Electron了。

下面咱們就來看看Electron包內的cli.js是如何啓動Electron的。

5. 使用命令啓動Electron

當開發者在當前項目下執行npm run dev時,其實就是執行electron.cmd批處理文件,

並傳入了一個命令行參數./index.js(這個文件咱們尚未建立,

不過不要緊,在這一小節裏這個文件並非重點)。

咱們知道electron.cmd批處理指令就是用node執行了node_modules\electron\cli.js文件,

同時也把命令行參數複製過去了。那麼咱們就看看cli.js的執行邏輯。

cli.js中最重要的邏輯代碼以下(爲了便於理解,我對這段代碼略有改動):

var proc = require('child_process')

var child = proc.spawn(electronExePath, process.argv.slice(2), {

stdio: 'inherit',

windowsHide: false

})

這段代碼就是使用Node.jschild_process對象建立了一個子進程,

讓子進程執行Electron的可執行文件,並把當前進程的命令行參數傳遞給了這個子進程。

命令行參數之因此從第三位開始取,是由於按照Node.js的約定,process.argv的第一個值爲process.execPath

第二個值爲正被執行的 JavaScript 文件的路徑,因此第三個值纔是咱們須要的./index.js

值得注意的是cli.js文件的首行代碼:

#!/usr/bin/env node

這行代碼是一個Shebang行(https://en.wikipedia.org/wiki/Shebang_(Unix)),

是類Unix平臺上的可執行純文本文件中的第一行,

經過#!前綴後面的命令行告訴系統將該文件傳遞給哪一個解釋器以供執行。

雖然Windows不支持Shebang行,但由於這是npm的約定,因此這一行代碼仍然是必不可少的。

至於Electron的可執行程序是如何接收這個參數,如何執行這個參數指向的程序文件的,咱們後文會有詳細描述。

6. Electron的版本管理方式

Electron 2.0.0以來,Electron的版本管理方式遵循semver的管理規則,

semver是 語義化版本規範(https://semver.org/lang/zh-CN/)的一個實現,

這是一個由npm的團隊維護的版本管理規範,它實現了版本和版本範圍的解析、計算、比較。

semver的版本號內容分爲主版本號次版本號修訂號三個部分,中間以點號分割

版本號遞增規則以下:

  • 主版本號:當作了不兼容修改時遞增;
  • 次版本號:當作了向下兼容的功能性更新時遞增;
  • 修訂號:當作了向下兼容的問題修正時遞增;

Electron則在這個約束的前提下增長了以下遞增規則:

主版本號更新規則

次版本號更新規則

修訂號更新規則

Electron有不兼容的修改時遞增

Electron兼容性更新時遞增

Electron問題修復時遞增

Node.js主版本號更新時遞增

Node.js次版本號更新時遞增

Node.js修訂版本號更新時遞增

Chromium更新時遞增

 

爲Chromium打補丁時更新

推薦你們使用穩定狀態的最新版本的Electron

若是已經安裝了老版本的Electron或者發現Electron有可用的更新(關注Electron官網的發佈頁面:https://www.electronjs.org/releases/stable可得到更新信息),

你們可使用以下指令更新本地工程的Electron版本:

npm install --save-dev electron@latest

Electron團隊承諾只維護最近的三個大版本,

好比本文發稿時Electron最新版本爲v9.2.0

那麼Electron團隊只會維護v9.x.xv8.x.xv7.x.xv6.x.x則再也不維護,當v10.x.x發佈以後,v7.x.x也再也不維護了。

並且目前Electron版本發佈至關頻繁,平均一到兩週就會有一個新的穩定版本發佈,

大量的更新不只僅帶來了更多的新功能、解決了更多的問題,

也意味着你所使用的版本即將成爲無人理睬的版本了,這也是爲何我推薦你們緊跟官方團隊版本發佈步伐的緣由。

咱們經過npm包管理工具安裝的Electron依賴包都是穩定版本,

除穩定版本外,Electron團隊還維護着beta版本和nightly版本,

這是Electron團隊和一些激進的開發者的演武場,除非特別須要,不推薦在商業項目中使用這些版本。

另外Electron官方github倉儲的issue頁面(https://github.com/electron/electron/issues)也時常會置頂一些重要更新事項,

與社區的開發者一塊兒討論更新方案的細節,等最終方案敲定後,則逐步推進更新落地。

好比近期很是重要的移除remote模塊的更新需求,就是置頂在這個頁面的。

這是開發者持續關注官方動向的最佳途徑,

一旦發現有不同意(deprecate)的內容或破壞性(breaking)的更新被置頂在這個頁面,

就應該儘可能在項目當中避免使用它們。

 

最後,推薦一下我本身的新書《Electron實戰》

http://product.dangdang.com/28547952.html

相關文章
相關標籤/搜索