Electron是一個(gè)優(yōu)秀的跨平臺(tái)桌面端應(yīng)用的框架,官網(wǎng)給出的簡紹很簡單:使用 JavaScript, HTML 和 CSS 構(gòu)建跨平臺(tái)的桌面應(yīng)用。好多朋友也想試試使用前端技術(shù)來做一個(gè)桌面應(yīng)用,但是往往在安裝的時(shí)候就直接報(bào)錯(cuò)了,大多數(shù)的錯(cuò)誤是:
Error: read ECONNRESET
# 或者
Error: Electron failed to install correctly ...
解決辦法
解決辦法也是簡單的,如果你是安裝失敗了,那么先把node_modules中的electron刪掉,然后重新開始下面的步驟。
- 設(shè)置淘寶鏡像源(推薦使用nrm,這一步是為了保證其他依賴不報(bào)錯(cuò))
npm install -g nrm
nrm use taobao
- 設(shè)置環(huán)境變量并安裝
# Mac系統(tǒng)
ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
npm install
# Windows系統(tǒng)
# 全局依賴cross-env為了把參數(shù)寫入環(huán)境變量 當(dāng)然直接修改環(huán)境變量也可以
npm install -g cross-env
cross-env ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron
npm install
然后稍等片刻就可以了。
深入研究
通過上面的設(shè)置基本上就解決了Electron安裝的問題,現(xiàn)在我們可以深入一下,看看它是怎么解決的。首先我們進(jìn)入它的源代碼,這個(gè)里面有一個(gè)npm的文件夾,這個(gè)路徑下存放的就是npm上的Electron依賴。打開看看你會(huì)發(fā)現(xiàn)只有幾個(gè)文件,沒錯(cuò)Electron的依賴包就這么點(diǎn)東西。

那么這么點(diǎn)東西怎么做跨平臺(tái)的應(yīng)用的呢?首先看一下package.json文件,為了方便查看,我拷貝過來一份,如下:
{
"main": "index.js",
"types": "electron.d.ts",
"bin": {
"electron": "cli.js"
},
"scripts": {
"postinstall": "node install.js"
},
"dependencies": {
"@electron/get": "^1.0.1",
"@types/node": "^12.0.12",
"extract-zip": "^1.0.3"
},
"engines": {
"node": ">= 8.6"
}
}
內(nèi)容很少,但有2個(gè)部分很重要。一個(gè)是script,可以看到里面有一個(gè)postinstall的鉤子命令,這條命令會(huì)在下載完依賴以后執(zhí)行一下,也就說當(dāng)依賴安裝完后會(huì)執(zhí)行node install.js。另外一個(gè)重要的部分就是bin,它指定了運(yùn)行全局依賴時(shí)的入口文件,也就是cli.js文件,我們稍后再說這個(gè)。
先簡單的看一下install.js,里面最主要的部分是調(diào)用了方法downloadArtifact,用來下載跟平臺(tái)相關(guān)的Electron可執(zhí)行文件。下載完后調(diào)用extractFile方法,把文件解壓了,最后在path.txt中把執(zhí)行文件的路徑寫進(jìn)去,這個(gè)路徑下是不同平臺(tái)下的可執(zhí)行文件的路徑。

最后我們看一下他是從哪里下載的。首先downloadArtifact方法是在@electron/get依賴?yán)锩?。我們進(jìn)入到src/index中。

此時(shí)我們可以看到url是通過getArtifactRemoteURL方法獲取的,然后我們看一下getArtifactRemoteURL方法,源碼在這里。

在getArtifactRemoteURL方法中,可以看到,基礎(chǔ)路徑base是通過mirrorVar函數(shù)返回的,默認(rèn)情況是沒有nightly的,所以默認(rèn)情況下是下面這個(gè)樣子的:
process.env[`NPM_CONFIG_ELECTRON_MIRROR`] ||
process.env[`npm_config_electron_MIRROR`] ||
process.env[`npm_package_config_electron_mirror`] ||
process.env[`ELECTRON_MIRROR`] ||
options['mirror'] ||
defaultValue
而我們很少傳入env的,options也沒有mirror,所以通常是defaultValue,具體值如下:

綜上,我們可以看到默認(rèn)情況下安裝的時(shí)候會(huì)在github的release處下載一個(gè)平臺(tái)相關(guān)的可執(zhí)行文件。但是往往在國內(nèi)github會(huì)很慢,所以這就導(dǎo)致了下載失敗的問題,如果我們的env中傳入ELECTRON_MIRROR,那就會(huì)走該值所對(duì)應(yīng)的地址,通常我們使用淘寶的鏡像http://npm.taobao.org/mirrors/electron。
同樣的,在構(gòu)建Windows系統(tǒng)的時(shí)候可能會(huì)用到依賴windows-build-tools,該依賴會(huì)安裝一個(gè)Python,這時(shí)可以使用淘寶的鏡像文件會(huì)更快一些:
# Macos
"PYTHON_MIRROR=http://npm.taobao.org/mirrors/python" npm install --global --production windows-build-tools
# Windows
# 若全局已經(jīng)安裝過cross-env那么就不需要再安裝了
npm install -g cross-env
cross-env PYTHON_MIRROR=http://npm.taobao.org/mirrors/python npm install --global --production windows-build-tools
淘寶更多的開源軟件的鏡像可以參考這里。
啟動(dòng)過程
下載過程基本上我們已經(jīng)明白了,現(xiàn)在說一下啟動(dòng)過程。一般啟動(dòng)Electron的時(shí)候調(diào)用的命令是electron .,而electron命令其實(shí)是調(diào)用依賴包中的cli.js文件,該文件內(nèi)容如下:
#!/usr/bin/env node
var electron = require('./')
var proc = require('child_process')
var child = proc.spawn(electron, process.argv.slice(2), { stdio: 'inherit', windowsHide: false })
child.on('close', function (code) {
process.exit(code)
})
const handleTerminationSignal = function (signal) {
process.on(signal, function signalHandler () {
if (!child.killed) {
child.kill(signal)
}
})
}
handleTerminationSignal('SIGINT')
handleTerminationSignal('SIGTERM')
其中var electron = require('./')的時(shí)候是引入當(dāng)前文件夾下的index.js文件,內(nèi)容如下:
var fs = require('fs')
var path = require('path')
var pathFile = path.join(__dirname, 'path.txt')
function getElectronPath () {
if (fs.existsSync(pathFile)) {
var executablePath = fs.readFileSync(pathFile, 'utf-8')
if (process.env.ELECTRON_OVERRIDE_DIST_PATH) {
return path.join(process.env.ELECTRON_OVERRIDE_DIST_PATH, executablePath)
}
return path.join(__dirname, 'dist', executablePath)
} else {
throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')
}
}
module.exports = getElectronPath()
index.js文件內(nèi)容很少,主要是返回了一個(gè)字符串的地址,也就是當(dāng)前路徑下的dist文件夾下的可執(zhí)行文件的路徑。還記得下載完后往path.txt里面寫了一個(gè)可執(zhí)行文件的路徑嗎?
我們再回到cli.js文件中,從代碼中可以看出,里面啟動(dòng)了一個(gè)子進(jìn)程,用子進(jìn)程啟動(dòng)Electron的可執(zhí)行文件,并且把參數(shù)傳進(jìn)去了。最后如果主進(jìn)程中斷或者出現(xiàn)錯(cuò)誤,那么就把子進(jìn)程殺掉。當(dāng)然你也可以直接雙擊dist下的可執(zhí)行文件,它會(huì)啟動(dòng)一個(gè)默認(rèn)的頁面。