安装 Electron 的门道
简介
因为国内的网络环境,我们构建 Electron 项目时,容易失败,通常遇到这种情况,我便粗暴的使用科学上网的方式来解决,对 Electron 安装过程中发生了什么一无所知,为了增强把控感,本文便来分析一下安装 Electron 时,发生了什么。
容易安装失败的原因
通常,我们会根据官方提示,通过下面方式来构建 Electron 开发环境:
npm init -y
npm install electron --save-dev
先提一下 --save-dev
。一般为工程项目安装 Electron 依赖时,都会将其安装为开发依赖 (--save-dev),当我们后续通过 electron-builder 打包时,electron-builder 会将生产依赖复制到安装包内,用户安装后,应用目录下会有相应的 node_modules 目录,安装为生产依赖的包会被放置在这个目录下,对于 Electron 而言,electron-builder 在打包时,会帮我们下载目标平台的 Electron 可执行文件,比如你要打包到 win32 平台,那么 electron-builder 就会去下载 win32 对应的 Electron 可执行文件并将其放置到正确的位置,如果我们以生产依赖安装 Electron,那么我们打出的包中,通常会有两份 Electron,一份是 electron-builder 为你准备的,一份在 node_modules 中,用户不需要多出的 Electron 文件,而这也增大了包的体积。
如果你没有任何科学上网的设置,那么在安装过程中,很容易出 504 (Gateway Time-out) 的错误,但我们安装其他 npm 包时,却不容易出现,为什么?
因为,Electron 是复合型的依赖包,与大多数 Node.js 依赖包不同,安装 Electron 的过程中,npm 还额外执行了 Electron 依赖包中配置文件定义的脚本代码,这些脚本会去 Github 仓库内下载相应的 Electron 可执行文件,这些文件都在国外的网站,大小在 80M 左右,因为国内的网络环境,就容易出现 504 的问题。
复合型依赖包的实现
Electron 是怎么将自己弄成一个复合型依赖包的?通过下面命令再安装一下并将安装过程的日志打印出来,因为刚刚使用 npm 安装过,目录脏了,最好清空目录文件,重新再来一次。
rm -rf *
npm init -y
npm install electron --save-dev --timing=true --loglevel=verbose
--timing=true --loglevel=verbose
会将安装的细节打印出来,从输出中可以发现,npm 一开始会通过 http fetch 从 https://registry.npmmirror.com/electron (因为配置过环境,所以你打印出了的不一定是这个 URL,但返回内容是一样的) 中获取一个 json 文件,这个 json 文件中中记录着 Electron 所有历史版本,用于校验用户要安装的版本,如果用户没有指定版本,则从该 json 文件中获取最新的版本进行安装。
npm 会将 Electron 相关的包文件和依赖从 npmmirror 服务器下载到 node_modules 目录中,npm 工具会检查 nodel_modules\electron\package.json,查看文件中是否有配置 npm 钩子脚本。
进一步讨论前,先理一下 npm 钩子的知识,很多人不知道,我们可以在 package.json 的 scripts 中定义 npm 钩子,这些钩子会在 npm 安装依赖包时被 npm 执行,npm 执行的钩子函数有:
preinstall: 依赖包安装之前执行
postinstall: 依赖包安装之后执行
preuninstall: 依赖包卸载之前执行
postuninstall: 依赖包卸载之后执行
poststart: 当 npm start 执行后触发
poststop: 当 npm stop 执行后触发
posttest: 当 npm test 执行后触发
看到 nodel_modules\electron\package.json 中的 scripts,如下图所示:
从图中可知,scripts 中有 postinstall,其对应的脚本为 node install.js
,npm 安装完 Electron 依赖包后,会执行 node install.js
,如下图所示:
简单而言,npm 安装完 Electron 依赖后,npm 还需要继续执行 install.js,install.js 会去下载 Electron 可执行文件,我们前面看见的安装错误,通常在这里抛出。
在 install.js 中,下载 Electron 可执行文件的关键方法如下:
const { downloadArtifact } = require('@electron/get');// downloads if not cached
downloadArtifact({version,artifactName: 'electron',force: process.env.force_no_cache === 'true',cacheRoot: process.env.electron_config_cache,checksums: process.env.electron_use_remote_checksums ? undefined : require('./checksums.json'),platform,arch
}).then(extractFile).catch(err => {console.error(err.stack);process.exit(1);
});
downloadArtifact 方法由 @electron/get 提供,@electron/get 是 Electron 包依赖的 npm 包(从 electron 的 package.json 便可知),而 @electron/get 也有相应的依赖,npm 会通过递归的方式去安装这些依赖。
在 npm3.x 之前,npm 通过嵌套式的文件结构来管理依赖包。例如,当前工程中安装了 package A 包,这个包又依赖于 package B,此时 npm 会在 package A 目录下创建 node_modules 文件夹并将 package B 放在其中,这种方式可能会出现多次下载同一依赖包的情况,比如多个包都依赖 package B,那么多个包的 node_modules 下都会有 package B(即多次下载),但却可以避免让人头疼的依赖版本问题,比如都依赖 package B,但依赖 package B 的版本不同,此时不同的目录天然的解决了版本问题。
但这里有个致命的问题便是 Windows 系统限制了文件路径的最长长度为 260 字符,npm3.x 之前的这种方式可能会出现嵌套很深的目录结构,从而被 Windows 限制,最终导致依赖包无法安装成功。
npm3.x 之后,npm 改为扁平结构,即无论是当前工程的依赖包,还是依赖包的依赖包,都会优先安装到当前工程的 node_modules 中,如果安装过程中,npm 发现了当前工程的 node_models 已经存在相同版本的依赖包,则跳过安装,只有在版本不同的情况下,才会安装新的依赖包。
npm 扁平结构处理依赖包的方式解决了重复安装和目录树过深的问题,但引出了依赖包版本冲突的问题,在 npm5.x 引入了 package lock 机制来解决这个问题,随着 npm 机制的复杂化,也出现了新的包管理工具,如 yarn,挑战当前 npm 的地位。
将注意力放回 downloadArtifact 方法,该方法会传入 JSON 对象作为参数,JSON 对象中的字段含义很直观,其中 force 属性表示是否要强制下载 Electron 二进制文件,如果当前程序的环境变量 (process.env) 的 force_no_cache 为 true,则无论本地是否有缓存,都会从 Electron 服务器下载相应的文件。
这个 JSON 对象最终组成一个 URL,用于下载相应版本的 Electron,其具体的逻辑在 node_modules/@electron/get/dist/cjs/index.js 的 downloadArtifact 方法中,除了下载 Electron 二进制文件,该方法还会下载 SHASUMS256.txt 用于校验 sha256,避免下载文件被篡改。
镜像与缓存
阅读 Electron 中关于安装的文档:https://www.electronjs.org/docs/latest/tutorial/installation
我们可以通过设置下载镜像或先下载 Electron 作为缓存文件来跳过 install.js 的下载。
先看缓存文件的解法,不同系统,存放缓存文件的位置不同:
Linux: ~/.cache/electron/ 或 $XDG_CACHE_HOME
MacOS: ~/Library/Caches/electron/
Windows: ~/AppData/Local/electron/Cache 或 $LOCALAPPDATE/electron/Cache
因为我使用 Windows 系统,所以缓存文件夹如下图所示:
上图中,因为我过去也使用过 electron,所以不太干净,阅读 Electron 文档,目前最新的形式是 [checksum]/[filename] 的形式,如下:
├── a91b089b5dc5b1279966511344b805ec84869b6cd60af44f800b363bba25b915
│ └── electron-v15.3.1-darwin-x64.zip
缓存文件夹中有 Electron 后,再次通过 npm 安装 electron 时,如果版本相同,便会跳过下载 Electron 的环节(除非设置了强制下载的环境变量)。
客观而言,这种方式还是有点麻烦的,每次下载不同版本的 Electron 都需要手动操作一下,所以我建议使用镜像的方式,既然国外网站慢,那么就通过国内镜像网站下载,其唯一的缺点可能就是不那么实时(最新版本可能需要等待镜像网站同步)。
相比于通过 npm 命令在本地设置使用的镜像,更建议使用.npmrc
的方式来设置镜像,创建.npmrc
文件,在其中输入如下内容:
electron_mirror=https://cdn.npm.taobao.org/dist/electron/
ELECTRON_BULLDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/
registry=https://registry.npm.taobao.org
npm 会基于.npmrc
中的内容从指定的镜像地址下载资源,在团队开发时,将.npmrc
加入 git 管理,这样团队中的每个人,都会使用相同的镜像环境去下载 Electron。
此外,更重要的一个意义是,通过.npmrc
我们实现了环境变量的共享,这样在写代码时,涉及环境变量的逻辑也是一致的。
运行 Electron
安装完 Electron 后,在项目的 package.json 中添加如下脚本:
"scripts": {"dev": "electron ./index.js"},
然后创建 index.js 并添加如下代码:
const {app, BrowserWindow} = require('electron')let win
app.whenReady().then(() => {// 新建窗口win = new BrowserWindow({width: 800,height: 600,})// 加载网站win.loadURL('http://hackpython.com')
})
在命令行工具中输入 npm run dev
,Electron 便会正常启动并载入相应的网站,效果如下:
至此,Electron 便安装完成了。
npm run dev 的背后
为什么通过 npm run dev
命令可以拉起 electron?背后的原理是啥?
当我们运行 npm run dev
指令时,npm 会新建一个命令环境并将当前项目下的 node_modules/.bin 目录加入到这个命令环境的环境变量中,然后再执行 scripts 配置中 dev 节点对应的命令,命令执行完后,会将 node_modules/.bin 从环境变量中删除。
在当前项目的 node_modules/.bin 目录中,有 3 个与 Electron 有关的文件:electron、electron.cmd、electron.ps1,这些文件都会加入命令环境中的环境变量中,所以我们可以执行 electron ./index.js
,该命令的其背后其实是 electron.cmd ./index.js
(Windows 系统中可以省略.cmd 扩展名)。
3 个文件中,electron 为 Linux 或 MacOS 提供,electron.cmd 是 Windows 批处理脚本,electron.ps1 是 Windows powershell 脚本。
我们通过 npm 安装 Electron 时,npm 处理会检查 npm 钩子脚本外,还会检查 package.json 中是否配置 bin 指令,Electron 的 package.json 中有 bin 指令,其内容如下:
"bin": {"electron": "cli.js"},
npm 发现这个配置后,会自动在 node_modules/.bin 目录中注入上面提及的 3 个 Electron 相关文件。
以 electron 脚本为例(shell 命令易读一些),其代码如下:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")case `uname` in*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esacif [ -x "$basedir/node" ]; thenexec "$basedir/node" "$basedir/../electron/cli.js" "$@"
else exec node "$basedir/../electron/cli.js" "$@"
fi
electron 脚本的作用是,通过 Node.js 执行 Electron 依赖包中的 cli.js 文件,并将用户输入的所有命令都传递给 cli.js,简单而言 electron ./index.js
相当于 node /node_modules/electron/cli.js ./index.js
,这便是我们执行 npm run dev
时,背后真正执行的命令。
cli.js
cli.js 才是启动 Electron 的关键,cli.js 中,关键代码如下:
const proc = require('child_process');// 创建子进程,通过子进程运行electron可执行文件,并将命令参数传递给electron
const child = proc.spawn(electron, process.argv.slice(2), { stdio: 'inherit', windowsHide: false });
child.on('close', function (code, signal) {if (code === null) {console.error(electron, 'exited with signal', signal);process.exit(1);}process.exit(code);
});
cli.js 中,利用 Node.js 的 child_process 库新建一个子进程,让子进程执行 Electron 的可执行文件,并将当前进程命令中参数传递给子进程,注意,传递的参数从第 3 位开始取,其中第 1 个参数:Node.exe 路径,第 2 个参数:JS 文件路径,即 cli.js 路径,这两个参数没有意义,所以不用传递给 electron,而第 3 个参数是./index.js,需要传递给 electron。
至此, electron.exe 变会去解释执行 inde.js 中的 JS 代码了,后面的逻辑也都交由 electron 完成。
结尾
这便是 electron 安装时背后的细节以及通过 npm 启动 electron 时背后的细节,后续会分享更多 electron 的文章,希望大家喜欢。
我是二两,下篇文章见。
安装 Electron 的门道相关推荐
- 如何在64位Ubuntu16.04系统下安装electron
一.Electron简介 Electron[官网https://electronjs.org/]是由Github开发,基于Chromium和Node.js,使用JavaScript.HTML和CSS等 ...
- 2021 npm安装Electron失败解决方法
npm直接安装Electron时,直接报如下错误 PS D:\Git\0.Repo\0.Gitee\electron-third-party-applications-notify> npm i ...
- npm安装electron时报Error: EPERM: operation not permitted, scandir.....
刚弄完vue脚手架,准备安装electron写个桌面应用,运行命令vue add electron-builder一直报错 Error: EPERM: operation not permitted, ...
- npm安装electron时卡死 ,而利用cnpm安装electron时失败(等一系列幺蛾子)
按照这个教程 ,安装cnpm 并执行 cnpm install -g electron 时出现 post install failure (部署失败) 原因是cnpm只会安装electron的js脚本 ...
- 安装electron项目的时候老是报错:Command failed.
问题描述 安装electron项目的时候,在下载包的时候报错 错误描述主要是 错误1:socket hang up error E:\theia\node_modules\electron: Comm ...
- Electron教程2_Windows上安装Electron
操作系统要求 Windows 7 以上 安装Nodeji V10 以上 安装Electron 克隆项目仓库 git clone https://github.com/electron/electron ...
- 安装electron时安装失败解决
错误描述: 在安装 electron 的时候,使用官方推荐的如下命令: npm install --save-dev electron 结果报错如下 npm ERR! code 1 npm ERR! ...
- cnpm安装Electron
cnpm安装Electron 国内使用npm安装Electron可能会报错,所以使用cnmp安装Electron 安装npm:brew install node 安装cnpm:npm install ...
- npm安装electron
先安装cnpm,再安装electron npm install cnpm -g --registry=https://registry.npm.taobao.org /usr/local/bin/bi ...
最新文章
- C#协变和逆变 - 译
- python 如何把小数变成百分数格式
- java xwork_xwork-core-2.3.4源码 - 源码下载|通讯/手机编程|J2ME|源代码 - 源码中国
- android 手机内存其他文件夹里,手机储存里的其他是什么以及应该怎样删除
- 赫夫曼树编码的算法及应用习题--数据结构
- 华中数控预计继续亏损 拟靠关联交易增收存疑
- “/etc/profile“ 无法保存 E212: Can‘t open file for writing
- 【电子书】Head First Java
- 上万网友自发利用“蔚蓝地图”报污染源
- 040-云E办_学习和安装FastDFS以及安装Nginx
- Crazy Binary String
- 百度地图滑动后获取屏幕的中心点百度坐标
- 基于数据指纹的增量式
- 群晖Bitwarden内网穿透https反向代理
- 国内开源镜像网站汇总
- Java新手小白入门篇 项目 - 深海杀手
- android系统 通知,Android中通知的使用-----Notification详解
- spring boot车辆管理系统毕业设计源码031034
- 分享一些Linux下监控软件
- 黎明觉醒火种测试服务器维护,黎明觉醒3月25日开启火种测试