electron 应用开发优秀实践

在团队中,我们因业务发展,需要用到桌面端技术,如离线可用、调用桌面系统能力。什么是桌面端开发?一句话概括就是:以 Windows 、macOS 和 Linux 为操作系统的软件开发。对此我们做了详细的技术调研,桌面端的开发方式主要有 Native 、 QT 、 Flutter 、 NW 、 Electron 、 Tarui 。其各自优劣势如下表格所示。
electron技术对比

一、技术背景


Electron 是多进程架构,架构具有以下特点:

由一个主进程和 N 个渲染进程组成
主进程承担主导作用,用于完成各种跨平台和原生交互
渲染进程可以是多个,使用 Web 技术开发,通过浏览器内核渲染页面
主进程和渲染进程通过进程间通信来完成各种功能
这里说下 Electron 进程间通信技术原理:

electron 使用 IPC (interprocess communication) 在进程之间进行通信,如下图所示:

二、应用技术选型

2.1 编程语言 Typescript

2.2 构建工具 Electron-Forge

2.3 Web 方案 Vue3 + Vite

2.4 monorepo方案 pnpm + turbo

2.5 数据库 lowdb

electron 应用数据库有非常多的选择如 lowdb 、 sqlite3 、 electron-store 、 pouchdb 、 dedb 、 rxdb 、 dexie 、 ImmortalDB 等。这些数据库都有一个特性,那就是无服务器。
给出四个最优选择,分别是 lowdb 、 sqlite3 、 nedb 、 electron-store , 理由如下:

  • lowdb: 生态、能力、性能三方面表现优秀, json 形式的存储结构, 支持 lodash 、 ramda 等 api 操作,利于备份和调用
  • sqlite3: 生态、能力、性能三方面表现优秀, Nodejs 关系型数据库第一选择方案
  • nedb: 能力、性能三方面表现优秀,缺点是基本不维护了,但底子还在,尤其操作是 MongoDB 的子集,对于熟悉 MongoDB 的使用者来说是绝佳选择。
  • electron-store: 生态表现优秀,轻量级持久化方案,简单易用

三、构建

3.1 应用图标生成

不同尺寸图标的生成有以下方法 Windows
软件生成: icofx3
网页生成: https://tool.520101.com/diannao/ico/(opens new window)

不同尺寸图标的生成有以下方法 MacOS
软件生成: icofx3
网页生成: https://tool.520101.com/diannao/ico/(opens new window)
命令行生成: 使用 sips 和 iconutil 生成

3.2 二进制文件构建

3.3 按需构建

3.4 性能优化

主要是构建速度和构建体积优化,构建速度这块不好优化。本文重点说下构建体积优化,这里拿 mac 系统举例说明, 在 electron 应用打包后,查看应用包内容
可以看出 asar 中的文件,就是我们构建后的项目代码,从图中可以看到有 node_modules 目录, 这是因为在 electron 构建机制中,会自动把 dependencies 的依赖全部打到 asar 中。

  • 将 web 端构建所需的依赖全部放到 devDependencies 中,只将在 electron 端需要的依赖放到 dependencies
  • 将和生产无关的代码和文件从构建中剔除
  • 对跨平台使用的二进制文件,如 ffmpeg 进行按需构建(上文按需构建已介绍)
  • 对 node_modules 进行清理精简
    这里提下第 4 点,如何对 node_modules 进行清理精简呢?

如果是 yarn 安装的依赖,我们可以在根目录使用下面命令进行精简:

 yarn autoclean -I
 yarn autoclean -F

如果是 pnpm 安装的依赖,第 4 点应该不起作用了。我在项目中使用 yarn 安装依赖,然后执行上述命令后,发现打包体积减少了 6M , 虽然不多,但也还可以。

四、更新

4.1 全量更新

通过下载最新的包或者 zip 文件,进行软件更新,需要替换所有的文件。

按照流程图去实现,我们需要做以下事情:

  • 开发服务端接口,用来返回应用最新版本信息
  • 渲染进程使用 axios 等工具请求接口,获取最新版本信息
  • 封装更新逻辑,用来对接口返回的版本信息进行综合比较,判断是否更新
  • 通过 ipc 通信将更新信息传递给主进程
  • 主进程通过 electron-updater 进行全量更新
  • 将更新信息通过 ipc 推送给渲染进程
  • 渲染进程向用户展示更新信息,若更新成功,则弹出弹窗告诉用户重启应用,完成软件更新

4.2 增量更新

通过拉取最新的渲染层打包文件,覆盖之前的渲染层代码,完成软件更新,此方案只需替换渲染层代码,无需替换所有文件。

  • 渲染进程定时通知主进程检测更新
  • 主进程检测更新
  • 需要更新,则拉取线上最新包
  • 删除旧版本包,复制线上最新包,完成增量更新
  • 通知渲染进程,提示用户重启应用完成更新
  • 全量更新和增量更新各有优势,多数情况下,采用增量更新来提高用户更新体验,同时使用全量更新作为兜底更新方案。

五、性能优化

5.1 启动时优化

  • 使用 v8-compile-cache 缓存编译代码
  • 优先加载核心功能,非核心功能动态加载
  • 使用多进程,多线程技术
  • 采用 asar 打包:会加快启动速度
  • 增加视觉过渡:loading + 骨架屏

5.1.1 使用 v8-compile-cache 缓存编译代码

[其他使用方法请查看此链接文档 ](https://www.npmjs.com/package/v8-compile-cache(opens new window))

5.1.2 优先加载核心功能,非核心功能动态加载

export function share() {
  const kun = require('kun')
  kun()
}

5.2 运行时优化

  • 对渲染进程 进行 Web 性能优化
  • 对主进程进行轻量瘦身

5.2.1 对渲染进程 进行 Web 性能优化

用一个思维导图来完整阐述如何进行 Web 性能优化,如下图所示:

5.2.2 对主进程进行轻量瘦身

核心方案就是将运行时耗时、计算量大的功能交给新开的 node 进程去执行处理。

const { fork } = require('child_process')
let { app } = require('electron')
function createProcess(socketName) {
  process = fork(`xxxx/server.js`, [
    '--subprocess',
    app.getVersion(),
    socketName
  ])
}

const initApp = async () => {
  // 其他初始化代码...
  let socket = await findSocket()
  createProcess(socket)
}

app.on('ready', initApp)

通过以上代码,将耗时、计算量大的功能,放在 server.js ,然后再 fork 到新开 node 进程中进行处理。

六、质量保障

6.1 自动化测试

6.2 崩溃监控

对于 GUI 软件,尤其桌面端软件来说,崩溃率非常重要,因此需要对崩溃进行监控。

崩溃监控技巧

  • 渲染进程崩溃后,提示用户重新加载
  • 通过 preload 统一初始化崩溃监控
  • 主进程、渲染进程通过 process.crash() 进行模拟崩溃
  • 对崩溃日志进行收集分析
  • 崩溃监控做好后,如果发生崩溃,该如何治理崩溃呢?

6.3 崩溃治理

崩溃治理难点:

  • 定位出错栈困难:Native 错误栈,无操作上下文
  • 调试门槛高:C++ 、 IIdb/GDB
  • 运行环境复杂:机器型号、系统、其他软件

崩溃治理技巧:

  • 及时升级 electron
  • 用户操作日志和系统信息
  • 复现和定位问题比治理重要
  • 把问题交给社区解决,社区响应快
  • 善于用 devtool 分析和治理内存问题

七、安全

7.1 源码泄漏

目前 electron 在源码安全做的不好,官方只用 asar 做了一下很没用的源码保护,到底有多没用呢?

你只需要下载 asar 工具,然后对 asar 文件进行解压就可以得到里面的源码了,如下图所示:

7.2 asar

asar 是一种将多个文件合并成一个文件的类 tar 风格的归档格式。Electron 可以无需解压整个文件,即可从其中读取任意文件内容。

7.3 源码保护

避免源码泄漏,按照从低到高的源码安全,可以分为以下程度

  • asar
  • 代码混淆
  • WebAssembly
  • Language bindings
    其中,Language bindings 是最高的源码安全措施,其实使用 C++ 或 Rust 代码来编写 electron 应用代码,通过将 C++ 或 Rust 代码编译成二进制代码后,破译的难度会变高。这里我说下如何使用 Rust 去编写 electron 应用代码。
posted @ 2024-05-09 10:11  boygdm  阅读(80)  评论(0编辑  收藏  举报