最近公司想搞 electron,我来之前其实有搞过,不过也就做得应付了事。现在有时间了,就想让我规范化起来。
electron 简介
官方描述:
Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。
从官方的描述当中,总结出来一些做 electron 之前需要熟悉的基本知识
- node.js
- javascript
- html
- css
以上只是基本要求,若是在公司里面做项目开发,以下基本前端知识还是要熟悉
- 前端框架,angular/react/vue,根据具体项目选定
- typescript,这年头还有做前端不会 ts 的吗?
- sass/less/scss的一种,一般less比较多
- material design / ant-design,基本各厂会根据设计师要求制作自己的组件库
- npm/yarn
- git
对 electron 有了大概的了解,且一些基本工作准备就绪,就可以开始了
纯 ts 搭建 electron
首先声明一下我的环境:
- 系统:windows
- node: v16.15.0
- yarn: 1.22.18
创建 package.json
yarn init
注意
author
和description
两个字段必须写一些东西,空字符串也不行。这是因为在后续的构建会需要这两个字段。虽然构建时的报错写得还算清晰,但连官网都提示了这两个字段的重要性,我们就照着做就好了。main
字段需修改为out/electron-app.js
设置 electron 镜像源,下载 electron 和其他依赖
项目根目录创建 .yarnrc
文件,内容如下:
# default mirror
registry https://registry.npm.taobao.org/
# electron relatives files are downloaded from taobao mirror
electron_mirror "https://npm.taobao.org/mirrors/electron/"
ELECTRON_BUILDER_BINARIES_MIRROR http://npm.taobao.org/mirrors/electron-builder-binaries/
若不指定,electron 下载是从外网下的,比较慢,且可能会出错。包括后续需要的 electron-builder
在下载过程中有一些 binary 是从 github下的,比较慢。换成淘宝的镜像源就快多了。
我用的是 yarn
, 所以写的是 .yarnrc
,若使用的是 npm
, 则写 .npmrc
是一样的。
yarn add electron -D
yarn add @types/electron -D
yarn add typescript -D
yarn add @types/node -D
创建各 electron 所需文件
electron 主进程文件 electron-main.ts
import {BrowserWindow, app, ipcMain, dialog} from 'electron'
import {join} from 'path'
import {ElectronChannel} from './ipc'
export const main = () => {
onReady('dev')
appListens()
}
const onReady = (type: 'dev' | 'prod') => {
app.whenReady().then(() => {
const mainWindow = createWindow(type)
mainWindowListens(mainWindow)
})
}
const mainWindowListens = (mainWindow: BrowserWindow) => {
ipcMainHandles(mainWindow)
ipcMainOnS(mainWindow)
}
const ipcMainOnS = (mainWindow: BrowserWindow) => {
}
const ipcMainHandles = (mainWindow: BrowserWindow) => {
ipcMain.handle(ElectronChannel.openDialog, () => {
dialog.showOpenDialog(mainWindow).then(v => {
console.log(v)
})
})
}
const createWindow = (type: 'dev' | 'prod' = 'dev') => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: join(__dirname, 'preload.js')
}
})
if (type === 'dev') {
win.loadURL('http://localhost:8000')
win.webContents.openDevTools()
} else if (type === 'prod') {
const path = join(__dirname, 'dist', 'index.html')
win.loadFile(path)
}
return win
}
const appListens = () => {
app.on('window-all-closed', () => {
if (process.platform !== 'darwin')
app.quit()
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0)
createWindow()
})
}
electron app 入口文件 electron-app.ts
import {main} from './electron-main'
main()
渲染进程与主进程的沟通文件 preload.ts
import {ipcRenderer, contextBridge} from 'electron'
import {ElectronChannel, electronApi} from './ipc'
const api: IElectionApi = {
openDialog: () => ipcRenderer.invoke(ElectronChannel.openDialog),
startDrag: (fileName) => {
ipcRenderer.invoke(ElectronChannel.onDragStart, fileName)
}
}
contextBridge.exposeInMainWorld(electronApi, api)
进程间通信的定义文件 ipc.ts
export const electronApi = 'electronApi'
export enum ElectronChannel {
openDialog = 'openDialog',
onDragStart = 'onDragStart',
}
为了在前端项目中更方便地使用暴露的 electron api,也为了规范化,定制的文件 renderer.d.ts
export {}
import {electronApi} from './ipc'
declare global {
interface IElectionApi {
openDialog: () => Promise<any>
startDrag: (fileName: string) => void
}
interface Window {
[electronApi]: IElectionApi,
}
}
创建 ts 配置文件 tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"outDir": "out",
"paths": { },
"removeComments": false,
"sourceMap": true,
"strict": true,
"suppressImplicitAnyIndexErrors": true,
"target": "esnext"
},
"include": [
"ipc.ts",
"electron-app.ts",
"electron-main.ts",
"preload.ts",
"renderer.d.ts",
],
"exclude": [
"node_modules",
"umi",
]
}
搭建一个前端项目
这块用什么都行,我这里用的是umi。跟着官方教程走一遍
注意:在项目中新建一个目录(我这里新建的目录叫 umi
),在该目录里面搭建 umi 的前端项目
所有文件创建完毕,开始将 electron 跑起来
把 umi 项目跑起来
cd umi
yarn && yarn start
修改根目录 package.json
,script
字段新增字段 electron:dev
"scripts": {
"electron:dev": "tsc && electron out/electron-app.js"
},
把 electron 跑起来
yarn && yarn run electron:dev
过一会就能打开一个客户端,内容是 umi 的界面。
构建 – electron-builder
下载 electron-builder
yarn add electron-builder -D
根目录下创建构建的配置文件 electron-builder.yml
更多配置文件参考官方文档
appId: "haha.app"
asar: false
win:
target: portable
electronDownload:
# version: 0.0.2
# isVerifyChecksum: false
platform: win32
package.json 文件 script 字段新增字段 electron:build
"scripts": {
"electron:dev": "tsc && electron out/electron-app.js",
"electron:build": "tsc && electron-builder",
"electron:build:debug": "DEBUG=electron-builder electron-builder"
},
进入 umi 项目,将该项目打包构建,生成文件夹 umi/dist/
cd umi
yarn build
返回项目根目录,构建 electron 客户端
yarn run electron:build
过一会就能在项目根目录 dist
文件夹看到文件 my-electron 1.0.0.exe
,windows下双击即可打开
展望未来 – auto update
electron 实际的更新场景应该是两个。
- 每次都把最新的客户端生成,然后发送给用户。
- 客户端自行发现有更新,提醒用户下载更新。
这两种场景适用不同场合。我认为第一种场景适用于用户的环境比较封闭,甚至处于离线的状态。对于外来的软件,都是由网管统一安装,这样更新虽然麻烦一些,但是胜在稳定。第二种场景适用于用户量很大,而且可能很分散,没有特殊要求的话让用户自行更新其实效率会更高。
暂时我还没走通自动更新的流程,看了一下网上大概思路有两个
- exe全量更新
- 局部热更新
而更新的方案及使用的库大概有几种
- 官方推荐的方案,看这里
- electron-updater
待我走通之后,找到最佳实践,再来更新一篇吧