说起 Electron,大家能定不会感觉到陌生,庞大的体积,内置浏览器,Hello World 都有 200 M... 我个人是很反感跨段应用的,虽然对于开发来说,节省了很多时间,但是站在用户的角度来讲,体验就不是那么称心如意了。但是最近一些业务需要用到 Electron,折腾过程中也踩了不少坑,总结一下。
开发环境的搭建
平时我们在开发前端应用时,一般都是使用 Webpack 去打包,在开发环境中,也是由 Webpack dev server 来实现 HMR。在 Electron 中也是可以使用 Webpack 的。
我们使用 electron-wepack
包,简单搭建一下环境。
shell
代码语言:javascript复制1yarn add source-map-support
2yarn add -D electron electron-webpack electron-builder webpack
COPY
然后我们参考这个项目结构建立目录:
代码语言:javascript复制1project/
2├─ resources/
3│ ├─ icon // 程序图标
4├─ src/
5│ ├─ main/ // 主进程
6│ │ └─ index.ts
7│ ├─ renderer/ // 渲染层(启动界面)
8│ └─ index.js
9└─ static/ // 静态资源
COPY
src
目录下的分别为存放 Electron 主进程逻辑(main) 和 渲染层(renderer)。入口文件必须为 index
或 main
TypeScript 支持 (可选)
shell
代码语言:javascript复制1yarn add electron-webpack-ts typescript -D
COPY
安装完以上依赖,electron-webpack
会识别支持 TypeScript。
渲染层
在 src/renderer/index.ts
中,你可以操作 DOM 树。electron-wepack
默认会提供一个空白的 HTML 文档,只有一个 #app
节点供你使用,你无法通过一般操作自定义一个入口 index.html
, 但是你也可以用其他手段达到这个目标,在此不多赘述 (参看 issue)。
ts
代码语言:javascript复制1// src/renderer/index.ts
2const $app = document.getElementById('app')!
3
4$app.textContent = 'Hello World'
COPY
主进程
在 src/main/index.ts
中, 简单建立一个 app
ts
代码语言:javascript复制1import { app, BrowserWindow } from 'electron'
2import { createWindow } from './common/window'
3
4let mainWindow: BrowserWindow
5app.on('ready', () => {
6 mainWindow = createWindow()
7 app.show()
8})
9
10app.on('window-all-closed', () => {
11 if (process.platform !== 'darwin') {
12 app.quit()
13 }
14})
15
16app.on('activate', function () {
17 if (mainWindow === null) {
18 createWindow()
19 }
20})
COPY
其次是 window.ts
,建立一个 window
ts
代码语言:javascript复制1import { BrowserWindow } from 'electron'
2import path from 'path'
3import { isDev } from '../utils'
4import { format } from 'url'
5export function createWindow() {
6 const mainWindow = new BrowserWindow({
7 height: 620,
8 width: 400,
9 webPreferences: { nodeIntegration: true }, // 一定要加!!!
10 })
11 if (isDev) {
12 mainWindow.loadURL(
13 `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`, // 开发环境
14 )
15 } else {
16 mainWindow.loadURL(
17 format({
18 pathname: path.join(__dirname, 'index.html'),
19 protocol: 'file',
20 slashes: true,
21 }),
22 )
23 }
24 return mainWindow
25}
COPY
脚本
在 package.json
中添加。
json
代码语言:javascript复制1{
2 "scripts": {
3 "prebuild": "rm -rf dist",
4 "build": "cross-env NODE_ENV=production electron-webpack",
5 "start": "electron-webpack dev",
6 "package": "yarn build && electron-builder build --publish never"
7 }
8}
COPY
执行 yarn start
。发现正确显示了 Hello World。
使用 yarn package
来生成 dmg 也是没有问题的。一般教程到此就结束了,但是我们的需求并不是这么简单,我们还需要配置其他,比如 app version,app icon,app sign key... 而这些配置也有很多坑。
配置
图标
应用图标需要不同大小的几张 png 以及 icns 等格式的图片,手动操作比较麻烦,我们可以用一张 png 去生成,使用 electron-icon-builder
工具就能轻松转换到我们想要的结果。
1npx electron-icon-builder -i ./path-your-icon-file.png -o output
COPY
代码语言:javascript复制1.
2├── icon.icns
3├── icon.ico
4├── icon.png
5└── icons
6 ├── 1024x1024.png
7 ├── 128x128.png
8 ├── 16x16.png
9 ├── 24x24.png
10 ├── 256x256.png
11 ├── 32x32.png
12 ├── 48x48.png
13 ├── 512x512.png
14 └── 64x64.png
COPY
把生成的文件放入 resources
文件夹内,如不存在则新建。
在 package.json
中加入 build
字段,用于配置 electron-builder
。
json
代码语言:javascript复制1{
2 "build": {
3 "appId": "com.innei.electron-template",
4 "productName": "template",
5 "extraMetadata": {
6 "main": "main.js" // **必须**
7 },
8 "copyright": "Copyright © 2019-2020 ${author}",
9 "mac": {
10 "category": "public.app-category.utilities"
11 },
12 "files": [
13 "package.json",
14 "resources/**/*", // **必须**
15 "static",
16 {
17 "from": "dist/main" // **必须**
18 },
19 {
20 "from": "dist/renderer" // **必须**
21 }
22 ],
23 "extends": null,
24 "dmg": {
25 "contents": [
26 {
27 "x": 130,
28 "y": 220
29 },
30 {
31 "x": 410,
32 "y": 220,
33 "type": "link",
34 "path": "/Applications"
35 }
36 ]
37 },
38 "win": {
39 "icon": "resources/icon.ico",
40 "target": [
41 "nsis",
42 "msi"
43 ]
44 },
45 "nsis": {
46 "oneClick": false,
47 "allowToChangeInstallationDirectory": true,
48 "installerIcon": "resources/icon.ico"
49 },
50 "linux": {
51 "target": [
52 "deb",
53 "rpm",
54 "AppImage"
55 ],
56 "category": "Development"
57 },
58 "directories": {
59 "buildResources": "resources",
60 "output": "release"
61 },
62 "extraResources": [
63 {
64 "from": "resources/", // **必须**
65 "to": "resources/" // **必须**
66 },
67 {
68 "from": "static",
69 "to": "static"
70 }
71 ]
72 }
73}
COPY
这里是个大坑,因为我们自定义了配置,覆盖了原来 electron-webpack
的配置,所以有几个地方是必须要这么写的,否则就会在打包之后无法显示 renderer 或者 找不到入口文件。这是我自己摸索出来的,比较 hack 的方法。因为我实在找不到答案。
如果你需要使用 __static
常量的话,
1{ // 也是必须的
2 "from": "static",
3 "to": "static"
4}
COPY
最后,附上 GitHub 地址:
https://github.com/Innei/electron-typescript-starter