开发者每每经过npm install(或 yarn add)指令完成为Node.js工程安装依赖包的工做,html
安装Electron也不例外,下面是npm和yarn的安装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 poststart当npm start执行后触发;
l poststop当npm stop执行后触发;
l posttest当npm test执行后触发;
详细的文档请参阅:https://docs.npmjs.com/misc/scripts
postinstall钩子定义在Electron包内的package.json中,代码以下:
"scripts": { "postinstall": "node install.js" }
install.js程序是Electron包内的一个重要程序,用于下载Electron的可执行文件及相关资源,下一小节咱们将讲解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值是否匹配,以免用户请求被截获,
下载到不安全的文件的状况(这方面的效用只能说聊胜于无),或者是下载过程意外终止,文件数据不完整的状况。
上文中咱们提到Electron的二进制文件压缩包下载成功后,会复制一份到缓存目录,以备下次使用。在Windows环境下,默认的缓存目录为:
C:\Users\Administrator\AppData\Local\electron\Cache
这是经过Node.js的os.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 哈希值文件的路径
细心的读者可能已经注意到了,个人路径并非github的url地址格式化得来的,
而是taobao的镜像源地址格式化得来的。下面咱们就介绍一下如何设置第三方镜像源。
@electron/get库下载Electron二进制文件包的地址被人为的分割成了三部分:
这三部分联合起来最终构成了下载地址,每一个部分都有其默认值,也有对应的重写该部分值的环境变量:
通常状况下,咱们只须要设置镜像部分的环境变量便可,
好比要设置淘宝的镜像源,
只须要把ELECTRON_MIRROR的环境变量的值设置为https://npm.taobao.org/mirrors/electron/便可,
设置方式与设置缓存目录的环境变量方式相同,此处再也不赘述。
Electron依赖包安装完成后,
npm会自动为其在node_modules/.bin路径下注入命令文件,
不带扩展名的electron文件是为linux和mac准备的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里都有相似以下这样的配置(Mocha为bin对象配置了两个属性,因此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的。
当开发者在当前项目下执行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.js的child_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的可执行程序是如何接收这个参数,如何执行这个参数指向的程序文件的,咱们后文会有详细描述。
自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.x,v8.x.x,v7.x.x。v6.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