如何手动安装 Electron

众所周知, 受 GFW 的影响, 我们有时候并不能很好地访问海外的一些网站, 比如 Github. 碰巧, 今日在学习如何使用 Electron 开发桌面端应用时, 遇到的第一个难题就是如何成功安装 Electron.

# 官方安装教程

我们先来看一下 官方教程 (opens new window):

现在, 您需要安装electron。 我们推荐的安装方法是把它作为您 app 中的开发依赖项, 这使您可以在不同的 app 中使用不同的 Electron 版本。 在您的app所在文件夹中运行下面的命令:

npm install --save-dev electron 除此之外, 也有其他安装 Electron 的途径。 请咨询 安装指南 (opens new window) 来了解如何用代理、镜像和自定义缓存。

简简单单一个 npm install --save-dev electron 就 OK 了. 无法完成 node install.js

GFW 爸爸很照顾你, 帮你屏蔽了海外的垃圾内容, 避免了你幼小的心灵受到伤害.

# 官方安装指南

幸好我们还有 安装指南 (opens new window): 自定义镜像和缓存 (opens new window) 中介绍到了可以指定 npm 镜像以加速 Electron 的安装:

ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"

赶紧试一试:

镜像更新不及时

Emmm, 阿里爸爸似乎没有把这个 zip 文件同步到镜像仓库里..

# 手动安装

只能手动安装了. 🙂

仔细观察前面安装时控制台里输出的日志, 可以发现 Electron 在尝试运行 C:\Users\Admin\AppData\Roming\npm\node_modules\electron\install.js 文件, 那我们就把这个文件打开, 看看能不能得到什么有用的信息:

#!/usr/bin/env node

const version = require('./package').version // 获取当前 Electron 版本

const fs = require('fs')
const os = require('os')
const path = require('path')
const extract = require('extract-zip')
const { downloadArtifact } = require('@electron/get')

if (process.env.ELECTRON_SKIP_BINARY_DOWNLOAD) {
  process.exit(0)
}

const platformPath = getPlatformPath() // 获取当前平台的可执行文件路径

if (isInstalled()) { // 避免重复安装
  process.exit(0)
}

// downloads if not cached
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)
})

function isInstalled () {
  try {
  	// 判断 ${HOME}\AppData\Roming\npm\node_modules\electron\version 文件中的内容与去掉 v 前缀后与当前 Electron 版本是否一致
    if (fs.readFileSync(path.join(__dirname, 'dist', 'version'), 'utf-8').replace(/^v/, '') !== version) {
      return false
    }

	// 判断 ${HOME}\AppData\Roming\npm\node_modules\electron\path.txt 文件中的内容与可执行文件是否一致
    if (fs.readFileSync(path.join(__dirname, 'path.txt'), 'utf-8') !== platformPath) {
      return false
    }
  } catch (ignored) {
    return false
  }
  
  // 是否有环境变量覆盖
  const electronPath = process.env.ELECTRON_OVERRIDE_DIST_PATH || path.join(__dirname, 'dist', platformPath)
  
  return fs.existsSync(electronPath)
}

// unzips and makes path.txt point at the correct executable
function extractFile (zipPath) {
  return new Promise((resolve, reject) => {
  	// 将指定文件解压到 .\dist 文件夹内.
    extract(zipPath, { dir: path.join(__dirname, 'dist') }, err => {
      if (err) return reject(err)

      fs.writeFile(path.join(__dirname, 'path.txt'), platformPath, err => {
        if (err) return reject(err)

        resolve()
      })
    })
  })
}

function getPlatformPath () {
  const platform = process.env.npm_config_platform || os.platform()

  switch (platform) {
    case 'mas':
    case 'darwin':
      return 'Electron.app/Contents/MacOS/Electron'
    case 'freebsd':
    case 'openbsd':
    case 'linux':
      return 'electron'
    case 'win32':
      return 'electron.exe'
    default:
      throw new Error('Electron builds are not available on platform: ' + platform)
  }
}

通过分析这个 install.js 文件, 我们得知:

  • 需要将特定的文件解压到 ${HOME}\AppData\Roming\npm\node_modules\electron\dist 目录中.
  • ${HOME}\AppData\Roming\npm\node_modules\electron\version 文件中的内容为 v${VERSION}.
  • ${HOME}\AppData\Roming\npm\node_modules\electron\path.txt 文件中的内容为 electron 可执行文件.

通过上面的几次尝试, 我们可以推断出这个待解压的文件很有可能是 Electron 在 GitHub Release 的附件中包含的文件 electron-v9.0.2-win32-x64.zip.

既然我们没法直接下载 GitHub Release 中的附件, 那我们就需要想想其他的办法. 比如具有中国特色社会主义的下载工具迅雷.

迅雷对于热点文件是有缓存的, 我们可以通过 GitHub Release 附件的下载地址尝试下载迅雷服务器上的缓存文件: 下载迅雷服务器上缓存的文件

嗯~~, 果然是有缓存的, 感谢迅雷

那么按照我们刚才分析出的结论, 一步步执行, 再在新的控制台中调用 electron 可执行文件进行测试: OK

🎉🎉🎉

终于, Electron 安装成功了, f感u谢k GFW.

# 最佳实践

将环境变量 ELECTRON_MIRROR https://cdn.npm.taobao.org/dist/electron/ 添加到系统的用户变量或系统变量中, 确保之后每次需要安装 Electron 时都使用镜像进行下载. 设置完成之后执行 npm install electron 即可获得最新版的 Electron.

使用环境变量

# 勘误

回顾文章时发现官方安装指南中在设置 ELECTRON_MIRROR 环境变量时的路径是以 '/' 结尾的, 而我在复制粘贴时漏掉了结尾的斜杠, 导致无法从阿里云的镜像上下载正确的文件.

设置正确的环境变量后成功下载到了镜像中的压缩包: 通过阿里云镜像下载 🎉 感谢阿里巴巴为国内开发环境做出的的基建贡献.