简介

个人看法,Electron.js是一个非常伟大的技术,可以让你通过简单的HTML,CSS,JavaScript就可以开发跨平台的桌面应用程序(Windows,Linux,Mac)

相关技术文档是这样解释Electron.js的:

Electron 框架的前身是 Atom Shell,可以让你写使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序。它是基于io.js 和 Chromium 开源项目,并用于在 Atom 编辑器中。Electron 是开源的,由 GitHub 维护,有一个活跃的社区。最重要的是,Electron 应用服务构建和运行在 Mac,Windows 和 Linux。

还有一点需要说明的是,Electron.js并不是唯一一个可以通过HTML,CSS,JavaScript就可以开发跨平台桌面应用程序的技术,还有一个node-webkit.js(简称nw.js)也是可以做到的,而且nw.js推出的时间还比Electron.js早。

很多朋友会问,为什么不选择比较早出的nw.js,而选择晚出的Electron.js呢?理由非常简单,Electron.js有非常成熟的产品了,比如Atom,Visual Studio Code,WordPress.com等等都是基于Electron.js开发的。而且Electron.js比nw.js文档更加齐全,更加明了。

最重要的还是有大量已经使用Electron.js开发的桌面应用程序。

最后,Electron.js的官方网址:http://electron.atom.io/

特别注意:由于我使用的是Windows 10 X64位操作系统,所以后续的所有教程或者代码都是是Window平台下的!

环境准备

node.js 安装

其实Electron.js就是基于Node.js开发的桌面应用程序,支持Node.js所有的语法,所以Node.js安装是必备的。

npm 是可以快速的找到你的包并载入你的Node.js环境中。

λ node -v
v12.16.2

λ npm  -v
7.21.0

Electron.js 安装

安装Electron.js有以下步骤:

必须启动以管理员运行的命令行

在命令行中输入 npm install -g electron-prebuilt 回车,即可安装Electron.js

cnpm install -g electron-prebuilt
Downloading electron-prebuilt to C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt_tmp
Copying C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt_tmp\_electron-prebuilt@1.4.13@electron-prebuilt to C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt
Installing electron-prebuilt's dependencies to C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt/node_modules
[1/2] extract-zip@^1.0.3 installed at node_modules\_extract-zip@1.7.0@extract-zip
[2/2] electron-download@^3.0.1 installed at node_modules\_electron-download@3.3.0@electron-download
execute post install 1 scripts...
[1/1] scripts.postinstall electron-prebuilt@1.4.13 run "node install.js", root: "C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\electron-prebuilt"
Downloading SHASUMS256.txt
[============================================>] 100.0% of 2.88 kB (2.88 kB/s)
[1/1] scripts.postinstall electron-prebuilt@1.4.13 finished in 13s
deprecate electron-download@3.3.0 › nugget@2.0.1 › request@^2.45.0 request has been deprecated, see https://github.com/request/request/issues/3142
deprecate electron-download@3.3.0 › nugget@2.0.1 › request@2.88.2 › har-validator@~5.1.3 this library is no longer supported
deprecate electron-download@3.3.0 › nugget@2.0.1 › request@2.88.2 › uuid@^3.3.2 Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
Recently updated (since 2021-11-09): 1 packages (detail see file C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt\node_modules\.recently_updates.txt)
  2021-11-15
    → electron-download@3.3.0 › nugget@2.0.1 › pretty-bytes@1.0.4 › meow@3.7.0 › normalize-package-data@2.5.0 › validate-npm-package-license@3.0.4 › spdx-correct@3.1.1 › spdx-license-ids@^3.0.0(3.0.11) (04:46:47)
All packages installed (153 packages installed from npm registry, used 16s(network 3s), speed 567.48KB/s, json 147(287.77KB), tarball 1.57MB)
[electron-prebuilt@1.4.13] link C:\Users\Administrator\AppData\Roaming\npm\electron@ -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\electron-prebuilt\cli.js

ps: 国内网速问题,使用 cnpm 代替 npm

等待安装完成之后,在命令行输入 electron -v 能够显示Electron.js版本号代表安装成功。

electron -v

v1.4.13

hello world

我们直接以官方例子开始。

# 克隆示例项目的仓库
$ git clone https://github.com/electron/electron-quick-start

# 进入这个仓库
$ cd electron-quick-start

# 安装依赖并运行
$ npm install && npm start

ps: 最后一步如果网速不好,可以拆分为2步:

cnpm install
npm start

可以启动一个基本应用。窗口内容如下:

Hello World!
We are using Node.js 16.5.0, Chromium 94.0.4606.81, and Electron 15.3.2.

源码分析

文件

index.html  LICENSE.md  main.js  node_modules/  package.json  package-lock.json  preload.js  README.md  renderer.js  styles.css

md 文本

LICENSE.md 对应开源协议

README.md 对应使用说明

此处暂时跳过。

package.json

最核心的部分。

{
  "name": "electron-quick-start",  //应用名称
  "version": "1.0.0", //版本
  "description": "A minimal Electron application", //描述
  "main": "main.js",  // 指定程序入口
  "scripts": {
    "start": "electron ."       // 启动命令
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^15.3.1"   // 版本依赖
  }
}

入口

  • main.js
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')

function createWindow () {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

理解起来也不难,app 准备完成之后,回调用 createWindow() 创建一个窗口。

窗口创建指定了大小,以及加载前的准备脚本 preload.js

主窗口对应的页面为 index.html

  • preload.js
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type])
  }
})
  • index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <link href="./styles.css" rel="stylesheet">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
  </body>
</html>

我们前面看到的内容是:

Hello World!
We are using Node.js 16.5.0, Chromium 94.0.4606.81, and Electron 15.3.2.

可见如 <span id="node-version"></span> 的内容,在 preload.js 文件中已经被替换掉了。

  • renderer.js

目前这个文件是空的。

// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.
  • styles.css

这个就是 css 样式文件,内容默认也是空的。

/* styles.css */

/* Add styles here to customize the appearance of your app */

electron-packager 打包

我们如何对上面的程序进行打包呢?

特点

1、支持平台有:Windows (32/64 bit)、OS X (also known as macOS)、Linux (x86/x86_64);

2、进行应用更新时,使用electron内置的autoUpdate进行更新

3、支持CLI和JS API两种使用方式;

安装 electron-packager

cnpm install --save-dev electron-packager

完成后,package.json 变化如下:

"devDependencies": {
    "electron": "^15.3.1",
    "electron-packager": "^15.4.0"
  }

ps: 当然,这里也可以手动修改。

打包命令

electron-packager <sourcedir> <appname> <platform> <architecture> <electron version> <optional options>

参数说明:

sourcedir:项目所在路径 appname:应用名称 platform:确定了你要构建哪个平台的应用(Windows、Mac 还是 Linux) architecture:决定了使用 x86 还是 x64 还是两个架构都用 electron version:electron 的版本 optional options:可选选项

构建打包命令

我们可以针对 windows 专门指定一个打包命令:

"scripts": {
    "start": "electron .",
	"package": "electron-packager ./ myapp --win --platform=win32 --arch=x64"
  },

运行打包:

cnpm run package

此时可能会比较慢:

λ cnpm run package

> electron-quick-start@1.0.0 package D:\code\electron-quick-start
> electron-packager ./ myapp --win --platform=win32 --arch=x64

Downloading electron-v15.3.2-win32-x64.zip: [==--------------------------------------------------------------------------------------------------] 2% ETA: 3568.4 seconds

全平台命令

当然,我们一次可以把 windows/mac/linux 的包都给打了。

electron-packager . App名称 --all -all --version=1.1.0

这个没成功,估计是网络问题。

electron-builder

介绍

electron-builder is A complete solution to package and build a ready for distribution Electron app for macOS, Windows and Linux with “auto update” support out of the box.

简单的说,electron-builder就是有比electron-packager有更丰富的的功能,支持更多的平台,同时也支持了自动更新。

除了这几点之外,由electron-builder打出的包更为轻量,并且可以打包出不暴露源码的setup安装程序。

特点:

1、electron-builder 可以打包成msi、exe、dmg文件,macOS系统,只能打包dmg文件,window系统才能打包exe,msi文件;

2、几乎支持了所有平台的所有格式;

3、支持Auto Update;

4、支持CLI和JS API两种使用方式;

安装依赖

$   cnpm install --save-dev electron-builder

or 官方推荐的 yarn 也类似:

yarn add electron-builder --save-dev

修改 package.json

在 package.json 中做如下配置

"build": {
    "appId": "com.xxx.app",
    "mac": {
      "target": ["dmg","zip"]
    },
    "win": {
      "target": ["nsis","zip"]
    }
},
"scripts": {
    "dist": "electron-builder --win --x64"
},

打包

执行打包命令:

cnpm run dist

报错:

> electron-builder --win --x64

Error: Cannot find module 'fs/promises'
Require stack:
- D:\code\electron-quick-start\node_modules\_builder-util@22.13.1@builder-util\out\fs.js
- D:\code\electron-quick-start\node_modules\_builder-util@22.13.1@builder-util\out\util.js
- D:\code\electron-quick-start\node_modules\_electron-builder@22.13.1@electron-builder\out\cli\cli.js
- D:\code\electron-quick-start\node_modules\_electron-builder@22.13.1@electron-builder\cli.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:980:15)

原因

这是nodejs版本太低导致的,查看 electron-builder 的代码可以发现,里面都是require(“fs/promises”)

这样的引用,但是旧版nodejs是require(“fs”).promises的引用方式

解决方案

(1)可以通过升级nodejs的方式解决,本人在win10系统下升级为nodejs14,可以正常打包。

(2)降低 electron-builder 的版本

控制在 v22.10.X 以内 v22.11.x以上的版本就会出现该问题

个人实战

卸载重装了一下 node

node -v
v16.13.0

重新安装依赖包。

λ cnpm run dist

> electron-quick-start@1.0.0 dist D:\code\electron-quick-start
> electron-builder --win --x64

  • electron-builder  version=22.13.1 os=10.0.19042
  • loaded configuration  file=package.json ("build" field)
  • writing effective config  file=dist\builder-effective-config.yaml
  • packaging       platform=win32 arch=x64 electron=15.3.2 appOutDir=dist\win-unpacked
  ⨯ Get "https://github.com/electron/electron/releases/download/v15.3.2/electron-v15.3.2-win32-x64.zip": dial tcp 20.205.243.166:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

依然报错,这个应该是网络问题。

https://github.com/electron/electron/releases/download/v15.3.2/electron-v15.3.2-win32-x64.zip 这个压缩包。

包下载慢的问题

下载的很慢,经常失败。

(1)修改配置

执行命令

cnpm config edit

在打开的文件中增加下面一行配置,然后保存关闭

··· electron_mirror=https://npm.taobao.org/mirrors/electron/ ···

(2)或者手动下载

打包过程中很明显有需要包的网络下载地址: https://github.com/electron/electron/releases/download/v8.2.0/electron-v8.2.0-win32-x64.zip

下载 electron-v8.2.0-win32-x64.zip 这个包,那只能通过离线下载,然后放到对应的目录里面

macOS: ~/Library/Caches/electron
Linux: ~/.cache/electron
windows: %LOCALAPPDATA%\electron\cache

需要注意的是,除了这个压缩包以外,还要把对应的SHASUMS256.txt-文件也下载下来放进去;(PS:txt文件需要按照截图格式命名才会生效,且里面只保留对应版本的编码)


重新安装:

λ cnpm run dist

> electron-quick-start@1.0.0 dist D:\code\electron-quick-start
> electron-builder --win --x64

  • electron-builder  version=22.13.1 os=10.0.19042
  • loaded configuration  file=package.json ("build" field)
  • writing effective config  file=dist\builder-effective-config.yaml
  • packaging       platform=win32 arch=x64 electron=15.3.1 appOutDir=dist\win-unpacked
  • downloading     url=https://npm.taobao.org/mirrors/electron/15.3.1/electron-v15.3.1-win32-x64.zip size=86 MB parts=8
  • downloaded      url=https://npm.taobao.org/mirrors/electron/15.3.1/electron-v15.3.1-win32-x64.zip duration=19.236s
  • default Electron icon is used  reason=application icon is not set
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/winCodeSign-2.6.0/winCodeSign-2.6.0.7z size=5.6 MB parts=1

类似的报错

下载

https://github.com/electron-userland/electron-builder-binaries/releases/download/winCodeSign-2.6.0/winCodeSign-2.6.0.7z

https://github.com/electron-userland/electron-builder-binaries/releases/download/nsis-3.0.4.2/nsis-3.0.4.2.7z"

依然会报错。

真的很麻烦,淦~

镜像的话,可以参考 https://npm.taobao.org/mirrors

类似上面,通过 cnpm config edit 修改一下镜像。

ELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/

重新执行命令:

λ cnpm run dist

> electron-quick-start@1.0.0 dist D:\code\electron-quick-start
> electron-builder --win --x64

  • electron-builder  version=22.13.1 os=10.0.19042
  • loaded configuration  file=package.json ("build" field)
  • writing effective config  file=dist\builder-effective-config.yaml
  • packaging       platform=win32 arch=x64 electron=15.3.1 appOutDir=dist\win-unpacked
  • default Electron icon is used  reason=application icon is not set
  • building        target=zip arch=x64 file=dist\electron-quick-start-1.0.0-win.zip
  • building        target=nsis file=dist\electron-quick-start Setup 1.0.0.exe archs=x64 oneClick=true perMachine=false
  • downloading     url=http://npm.taobao.org/mirrors/electron-builder-binaries/nsis-3.0.4.2/nsis-3.0.4.2.7z size=1.4 MB parts=1
  • downloaded      url=http://npm.taobao.org/mirrors/electron-builder-binaries/nsis-3.0.4.2/nsis-3.0.4.2.7z duration=1.055s
  • downloading     url=http://npm.taobao.org/mirrors/electron-builder-binaries/nsis-resources-3.4.1/nsis-resources-3.4.1.7z size=731 kB parts=1
  • downloaded      url=http://npm.taobao.org/mirrors/electron-builder-binaries/nsis-resources-3.4.1/nsis-resources-3.4.1.7z duration=723ms
  • building block map  blockMapFile=dist\electron-quick-start Setup 1.0.0.exe.blockmap

这次成功了。

打包结果

打包的内容都在 dist 文件夹。

$   cd dist && ls

builder-debug.yml   
builder-effective-config.yaml  
'electron-quick-start Setup 1.0.0.exe'     # 安装文件,默认在 C 盘
'electron-quick-start Setup 1.0.0.exe.blockmap'  
electron-quick-start-1.0.0-win.zip   # 压缩包,解压可以直接使用。
latest.yml     #更新相关的文件
win-unpacked

研究electron打包的过程中踩了不少坑,打包涉及到不少系统级别的问题,从最初的选型到最后的成功打包,看似是个简单的过程,但其中仍有一些需要注意到的地方,我在这里分两点说明:

devDependencies与dependencies的区别

dependencies 表示我们要在生产环境下使用该依赖,devDependencies 则表示我们仅在开发环境使用该依赖。

在打包时,一定要分清哪些包属于生产依赖,哪些属于开发依赖,尤其是在项目较大,依赖包较多的情况下。

若在生产环境下错应或者少引依赖包,即便是成功打包,但在使用应用程序期间也会报错,导致打包好的程序无法正常运行。W

npm与cnpm的区别

说到npm与cnpm的区别,可能大家都知道,但大家容易忽视的一点,是cnpm装的各种node_module,这种方式下所有的包都是扁平化的安装。

一下子node_modules展开后有非常多的文件。导致了在打包的过程中非常慢。

但是如果改用npm来安装node_modules的话,所有的包都是树状结构的,层级变深。

由于这个不同,对一些项目比较大的应用,很容易出现打包过程慢且node内存溢出的问题(这也是在解决electron打包过程中困扰我比较久的问题,最后想到了npm与cnpm的这点不同,解决了node打包内存溢出的问题,从打包一次一小时优化到打包一次一分钟,极大的提高了效率)。

所以建议大家在打包前,将使用 cnpm 安装的依赖包删除,替换成npm安装的依赖包。

参考资料

https://www.npmjs.com/package/electron-packager

https://www.kancloud.cn/winu/electron/154345

electron将vue项目打包成桌面应用(.dmg/.exe)

Electron 应用程序打包

electron应用生成exe程序并打包过程记录

electron打包:electron-packager及electron-builder两种方式实现(for Windows)

Electron-builder打包安装程序遇到的问题解决方案

electron-builder 打包出现 cannot find module fs/promises

electron-v8.2.1-win32-x64.zip 下载失败(npm install electron 安装失败)

如何解决Electron安装包下载慢的问题

electron-builder打包过程中报错——网络下载篇