本文目录
Electron 笔记
过去在工作中因需要,匆忙给政企部门封装过一个 Electron 客户端应用,整个过程非常“敏捷“,印象中有很多版本方面的差异和兼容问题,但没有总结,这里对着官网整个基础 mac 应用,学习整理下各方面功能实现及 API 调用。
官网:https://www.electronjs.org/
本文完成 DEMO : https://github.com/zzyxka/electron-demo-app
注:本文以 mac 平台为准,其他平台可能需要增加“补丁”类模板代码。
1. 简介&核心概念
Electron 是能够使用 前端技术(HCJ)
开发 跨平台桌面APP
的 开源
方案。
基于 Node.js 和 Chromium 内核,渲染一个 web 项目的桌面应用框架,同时具备 web 应用的渲染策略 和 Node.js 的后端能力,并提供了许多操作系统 API 以供使用,完成 web 应用做不到的操作系统/文件系统级别交互。
核心概念
- 主进程 - main process:简单理解为运行整个 APP 的 Node 进程
- 渲染进程 - renderer process:简单理解为被主进程加载的 前端 web 项目
- 特殊的
preload.js
- preload script:渲染前,最后能够同时使用 node / window&document 的机制,可以理解为从 Node 环境下带一些初始化参数给渲染进程进行首次渲染 - 进程间通信 - ipc:主进程(Node) 无法直接访问 渲染进程(web),反之亦然,二者需要交互,就需要用到进程间 ipc 通信
- “原生” API:从
electron
包中导出的各类用于程序创建、窗体、消息推送等 API
2. 编写应用
2.1 安装&初始化
1 | mkdir electron-app && cd electron-app |
和常规 node 项目一样,npm 初始化,npm 安装 electron 包,国内可能遇到注释问题,按注释替换 electron_mirror 镜像解决。
1 | { |
初始化:
- 和常规 node 项目一样,在 package.json scripts 中增加 start 启动本地调试
- package.json 中 main 作用为标识应用程序入口,在这里即 主进程 入口
- 创建 main.js 来编写主进程代码
- 创建 index.html 来作为视图/界面/渲染进程(当然可以是任何前端框架项目)
- 创建 preload.js 来完成渲染进程的初始化工作
2.2 主进程模板代码
编写主进程其实就是在编写 Node.js,需要关注:
- 加载渲染进程,即渲染视图页面/前端项目
- 不同操作系统的视图、表现、默认交互差异等,如:窗体关闭是否处于后台等不一致表现,需要在一些程序 生命周期 中,使用部分模板代码来处理
1 | const { app, BrowserWindow } = require('electron') |
2.3 渲染进程模板代码
编写渲染进程其实就是在编写前端,需要关注,怎么判断 web 处于 Electron 中
1 |
|
1 | // render.js |
2.4 Preload Script
能够同时访问主进程和渲染进程的“初始化”位置,需要关注:
- 为渲染进程安全地注入部分 属性 和 Node 能力
- 为渲染进程开启与主进程通信的 ipcRenderer
1 | const { contextBridge, ipcRenderer } = require('electron') |
2.5 进程间通信
利用 preload.js 中注入的 ipc 通信方法,进行主进程和渲染进程通信。
参考:
https://www.electronjs.org/docs/latest/api/ipc-main
https://www.electronjs.org/docs/latest/api/ipc-renderer
main.js
1 | app.whenReady().then(() => { |
render.js
1 | if (window.bridge) { |
2.6 “原生” API / UI 调用
以 Notification 为例,参考 https://www.electronjs.org/docs/latest/tutorial/notifications
1 | const { app, BrowserWindow, ipcMain, Notification } = require('electron') |
2.7 本地 DB 能力-sqlite
让 Electron 项目具备 sqlite 能力,实际上就是为 Node.js 项目添加 sqlite 能力。
https://www.npmjs.com/package/sqlite3
1 | npm install sqlite sqlite3 |
1 | const { app } = require('electron') |
2.8 日志管理
https://www.npmjs.com/package/electron-log
1 | npm install electron-log |
1 | const { app } = require('electron') |
2.9 应用打包
Electron 核心包里没有提供应用打包方案,需要使用其他方案完成打包。打包可以打出类似 windows 的安装程序,也可以打出类似 mac .app 的便携包。Electron Forge 是一款一体化(all-in-one)工具,用于打包和发布 Electron 应用程序。
1 | npm install --save-dev @electron-forge/cli |
执行完上述命令,会自动添加 package.json 中 scripts、forge.config.js 以及相关依赖,大多用于不同平台打包使用。
npm run make
npm run package
这两个命令都可以完成打包,区别参考:differentiate between make and package in Electron Forge
2.10 应用发布与版本更新
https://www.electronjs.org/docs/latest/tutorial/tutorial-publishing-updating
尚未实践
3. 问题记录&说明
除代码中注释说明内容外,过程中可能会遇到本章节提到的各类问题。
3.1 preload 注入的安全问题
1 | Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security |
web 控制台上述报错,通过访问链接,在 Security 章节可以得到”最佳实践-安全“的答案。
先简单看看这里都讲了哪些内容:
前端在浏览器下运行,被”沙盒“限制了足够的权限,往往前端代码并不会带来相关的严重安全问题。但 Electron 本质上是客户端应用,我们使用 JavaScript 能够做更大权限的事情,包括不限于 文件系统操作、shell 操作等等,与之而来的安全问题不得不重视。Electron 本身不会为此”负责“,需要开发者自己明确所引用的资源安全(规避具有 Node 能力能够”摧残“操作系统的危险)。
安全是 Electron、Chromium、Nodejs、依赖的 npm 第三方包等共同作用结果,所以为了安全需要遵循一些最佳实践:
- Electron 版本:保持版本更新
- 评估第三方依赖的安全性
- 编码安全,避免常见的安全问题,如XSS
隔离不受信任的内容,比如加载远程站点,确保站点受信安全。
安全检查清单:
… // https://www.electronjs.org/docs/latest/tutorial/security#checklist-security-recommendations
回到问题本身,要解决这个问题,需要:
- 不使用内联 Script
- 增加 CSP 标签
1 | <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
4. 常见场景配置
4.1 mac 顶栏设置
图像处理:https://www.electronjs.org/docs/latest/api/native-image
任务栏/托盘:https://www.electronjs.org/docs/latest/tutorial/tray
1 | const icon = nativeImage.createFromPath('public/icon.png') |
4.2 mac dock 应用图标
https://www.electronjs.org/docs/latest/api/app#appdock-macos-readonly
https://www.electronjs.org/docs/latest/api/dock
1 | if (process.platform === 'darwin') { |
4.3 设置应用程序图标
这里是指设置打包生成的应用程序图标,以 mac 为例。
首先准备要作为应用程序图标的图片,假设名字为
pic.png
mkdir tmp.iconset
,创建一个临时目录存放后续生成的图片执行命令生成各个尺寸图片
1
2
3
4
5
6
7
8
9
10sips -z 16 16 pic.png --out tmp.iconset/icon_16x16.png
sips -z 32 32 pic.png --out tmp.iconset/icon_16x16@2x.png
sips -z 32 32 pic.png --out tmp.iconset/icon_32x32.png
sips -z 64 64 pic.png --out tmp.iconset/icon_32x32@2x.png
sips -z 128 128 pic.png --out tmp.iconset/icon_128x128.png
sips -z 256 256 pic.png --out tmp.iconset/icon_128x128@2x.png
sips -z 256 256 pic.png --out tmp.iconset/icon_256x256.png
sips -z 512 512 pic.png --out tmp.iconset/icon_256x256@2x.png
sips -z 512 512 pic.png --out tmp.iconset/icon_512x512.png
sips -z 1024 1024 pic.png --out tmp.iconset/icon_512x512@2x.pngnpm i -g iconutil
通过
iconutil
生成icns文件iconutil -c icns tmp.iconset -o Icon.icns
设置 forge.config.js
1
2
3
4
5
6packagerConfig: {
asar: true,
overwrite: true,
name: 'ElectronApp', // 应用名
icon: 'public/Icon' // no file extension required (mac is .icns)
},