关于electron:electron-应用开发优秀实践

63次阅读

共计 8051 个字符,预计需要花费 21 分钟才能阅读完成。

vivo 互联网前端团队 -Yang Kun

一、背景

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

咱们最终的桌面端技术选型是 Electron,Electron 是一个能够应用 Web 技术来开发跨平台桌面利用的开发框架。

其技术组成如下:

Electron = Chromium + Node.js + Native API

各技术能力如下图所示:

整体架构如下图所示:

Electron 是多过程架构,架构具备以下特点:

  • 由一个主过程和 N 个渲染过程组成
  • 主过程承当主导作用,用于实现各种跨平台和原生交互
  • 渲染过程能够是多个,应用 Web 技术开发,通过浏览器内核渲染页面
  • 主过程和渲染过程通过过程间通信来实现各种性能

这里说下 Electron 过程间通信技术原理:

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

其提供了 IPC 通信模块,主过程的 ipcMain 和渲染过程的 ipcRenderer。

从 electron 源码中能够看出,ipcMain 和 ipcRenderer 都是 EventEmitter 对象,源码如下图所示:

看到源码实现,是不是感觉 IPC 不难理解了。知其本质,方可熟能生巧。

看到这,咱们回顾上文技术表格,看到 Electron 利用包体积大,那体积大的根本原因是什么呢?

其实这和 chromium 的框架设计无关,其对很多性能都没有宏管制,导致很难把宏大简单的细节性能去除掉,也造成了基于 chromium 的开发框架,如 electron、nwjs 打出的包起步就是 100 多 M。

综上,electron 具备跨端、基于 Web、超强生态等长处,是桌面端开发的优良计划之一。下文将介绍 electron 利用开发实践经验,包含利用技术选型和罕用性能。

二、利用技术选型

2.1 编程语言 Typescript

理由如下:

  • 针对开发者
  1. Javascript 的超集 – 无缝反对所有的 es2020+ 所有的个性,学习老本小
  2. 编译生成的 JavaScript 的代码放弃很好的可读性
  3. 可维护性明显增强
  4. 残缺的 OOP 的反对 – extends, interface,private,protect,public 等
  5. 类型即文档
  6. 类型的束缚,更少的单元测试的笼罩
  7. 更平安的代码
  • 针对工具
  1. 更好的重构能力
  2. 动态剖析主动导包
  3. 代码谬误查看
  4. 代码跳转
  5. 代码提醒补齐
  • 社区

大量的社区的类型定义文件 晋升开发效率

2.2 构建工具 Electron-Forge

理由:简略而又弱小,目前 electron 利用最好的构建工具之一。

这里提一下 electron-builder 其和 electron-forge 的介绍和区别,看下图所示:

两者最大的区别在于自由度,两者在能力上根本没什么差别了,从官网组织中的排序看,无意优先举荐 electron-forge。

2.3 Web 计划 Vue3 + Vite

咱们采纳的是 Vue3,同时应用 Vite 作为构建工具,具体长处,大家能够查看官网介绍,这套组合是目前支流的 Web 开发计划。

2.4 monorepo 计划 pnpm + turbo

目前的 monorepo 生态百花齐放,正确的实际办法应该是集大成法,也就是取各家之长,目前的趋势也是如此,各开源 monorepo 工具达成默契,专一本人善于的能力。

如 pnpm 善于依赖治理,turbo 善于构建工作编排。遂在 monorepo 技术选型上,我抉择了 pnpm 和 turbo。

pnpm 理由如下:

  • 目前最好的包管理工具,pnpm 排汇了 npm、yarn、lerna 等支流工具的精髓,并去其糟粕。
  • 生态、社区沉闷且弱小
  • 联合 workspace 能够实现 monorepo 最佳设计和实际
  • 在治理多我的项目的包依赖、代码格调、代码品质、组件库复用等场景下,表现出色
  • 在框架、库的开发、调试、保护方面,表现出色

相比于 vue 官网,在应用 pnpm 上,我加了 workspace。

turbo 理由如下:

  • 它是一个高性能构建零碎,领有增量构建、云缓存、并行执行、运行时零开销、工作管道、精简子集等个性
  • 具备十分优良的工作编排能力,能够补救 pnpm 在工作编排上的短板

2.5 数据库 lowdb

electron 利用数据库有十分多的抉择如 lowdb、sqlite3、electron-store、pouchdb、dedb、rxdb、dexie、ImmortalDB 等。这些数据库都有一个个性,那就是无服务器。

electron 利用数据库技术选型思考因素次要有以下 3 点:

  • 生态(使用者数量、保护频率、版本稳定度)
  • 能力
  • 性能
  • 其余(和使用者技术匹配度)

咱们通过以下渠道进行了相干调研

  • github 的 issues、commit、fork、star
  • sourcegraph 关键字搜寻后果数
  • npm 包下载量、版本公布
  • 官网和博客

给出四个最优抉择,别离是 lowdb、sqlite3、nedb、electron-store,理由如下:

  • lowdb: 生态、能力、性能三方面体现优良,json 模式的存储构造,反对 lodash、ramda 等 api 操作,利于备份和调用
  • sqlite3: 生态、能力、性能三方面体现优良,Nodejs 关系型数据库第一抉择计划
  • nedb: 能力、性能三方面体现优良,毛病是根本不保护了,但底子还在,尤其操作是 MongoDB 的子集,对于相熟 MongoDB 的使用者来说是绝佳抉择。
  • electron-store: 生态体现优良,轻量级长久化计划,简略易用

咱们应用的数据库选型是 lowdb 计划。

PS:提一下 pouchdb,如果须要将本地数据同步到远端数据库,能够应用 pouchdb,其和 couchdb 能够轻松实现同步。

2.6 脚本工具 zx

软件开发过程中,将一些流程和操作通过脚本来实现,能够无效地进步开发效率和幸福度。

依赖 node runtime 的优良抉择就两个:shelljs 和 zx,抉择 zx 的理由如下:

  1. 自带 fetch、chalk 等罕用库,应用方便快捷
  2. 多个子过程方便快捷、执行远端脚本、解析 md、xml 文件脚本、反对 ts,功能丰富且弱小
  3. 谷歌出品,大厂背景,生态十分沉闷

至此,技术选型就介绍完了,上面我将介绍 electron 利用的罕用性能。

三、构建

此局部次要介绍以下 5 点内容:

  • 利用图标生成
  • 二进制文件构建
  • 按需构建
  • 性能优化
  • 跨平台兼容

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 二进制文件构建

本章节内容是基于 electron-forge 论述的,不过原理是一样的。

在开发桌面端利用时,会有场景要用到第三方的二进制程序,比方 ffmpeg 这种。在构建二进制程序时,要关注以下两个留神项:

(1)二进制程序不能打包进 asar 中 能够在构建配置文件(forge.config.js)进行如下设置:

const os = require('os')
const platform = os.platform()
const config = {
  packagerConfig: {
    // 能够将 ffmpeg 目录打包到 asar 目录里面
    extraResource: [`./src/main/ffmpeg/`]
  }
}

(2)开发和生产环境,获取二进制程序门路办法是不一样的 能够采纳如下代码进行动静获取:

import {app} from 'electron'
import os from 'os'
import path from 'path'
const platform = os.platform()
const dir = app.getAppPath()
let basePath = ''
if(app.isPackaged) basePath = path.join(process.resourcesPath)
else basePath = path.join(dir, 'ffmpeg')
const isWin = platform === 'win32'
// ffmpeg 二进制程序门路
const ffmpegPath = path.join(basePath, `${platform}`, `ffmpeg${isWin ? '.exe' : ''}`)

3.3 按需构建

如何对跨平台二进制文件进行按需构建呢?

比方桌面利用中用到了 ffmpeg,它须要有 windows、mac 和 linux 的下载二进制。在打包的时候,如果不做按需构建,则会将 3 个二进制文件全部打到构建中,这样会让利用体积减少很多。

能够在 forge.config.js 配置文件中进行如下配置,即可实现按需构建,代码如下:

const platform = os.platform()
const config = {
  packagerConfig: {extraResource: [`./src/main/ffmpeg/${platform}`]
  },
}

通过 platform 变量来把对应零碎的二进制打到构建中,即可实现对二进制文件的按需构建。

3.4 性能优化

次要是构建速度和构建体积优化,构建速度这块不好优化。本文重点说下构建体积优化,这里拿 mac 零碎举例说明,在 electron 利用打包后,查看利用包内容,如下图所示:

能够看到有一个 app.asar 文件, 这个文件用 asar 解压后能够看到有以下内容:

能够看出 asar 中的文件,就是咱们构建后的我的项目代码,从图中能够看到有 node_modules 目录,这是因为在 electron 构建机制中,会主动把 dependencies 的依赖全部打到 asar 中。

所以联合上述剖析,咱们的优化措施有以下 4 点:

  1. 将 web 端构建所需的依赖全副放到 devDependencies 中,只将在 electron 端须要的依赖放到 dependencies
  2. 将和生产无关的代码和文件从构建中剔除
  3. 对跨平台应用的二进制文件,如 ffmpeg 进行按需构建(上文按需构建已介绍)
  4. 对 node_modules 进行清理精简

这里提下第 4 点,如何对 node_modules 进行清理精简呢?

如果是 yarn 装置的依赖,咱们能够在根目录应用上面命令进行精简:

yarn autoclean -I

yarn autoclean -F

如果是 pnpm 装置的依赖,第 4 点应该不起作用了。我在我的项目中应用 yarn 装置依赖,而后执行上述命令后,发现打包体积缩小了 6M,尽管不多,但也还能够。

至此,构建性能就介绍完了。

四、更新

本章节次要分为以下两个方面:

  1. 全量更新
  2. 增量更新

上面将顺次介绍上述两种更新

4.1 全量更新

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

整体设计流程图如下:

依照流程图去实现,咱们须要做以下事件:

  1. 开发服务端接口,用来返回利用最新版本信息
  2. 渲染过程应用 axios 等工具申请接口,获取最新版本信息
  3. 封装更新逻辑,用来对接口返回的版本信息进行综合比拟,判断是否更新
  4. 通过 ipc 通信将更新信息传递给主过程
  5. 主过程通过 electron-updater 进行全量更新
  6. 将更新信息通过 ipc 推送给渲染过程
  7. 渲染过程向用户展现更新信息,若更新胜利,则弹出弹窗通知用户重启利用,实现软件更新

4.2 增量更新

通过拉取最新的渲染层打包文件,笼罩之前的渲染层代码,实现软件更新,此计划只需替换渲染层代码,无需替换所有文件。

依照流程图去实现,咱们须要做以下事件

  1. 渲染过程定时告诉主过程检测更新
  2. 主过程检测更新
  3. 须要更新,则拉取线上最新包
  4. 删除旧版本包,复制线上最新包,实现增量更新
  5. 告诉渲染过程,提醒用户重启利用实现更新

全量更新和增量更新各有劣势,少数状况下,采纳增量更新来进步用户更新体验,同时应用全量更新作为兜底更新计划。

至此,更新性能就介绍完了。

五、性能优化

分为以下 3 个方面:

  1. 构建优化
  2. 启动时优化
  3. 运行时优化

构建优化在上文内容中,曾经具体介绍过了,这里不再介绍,上面将介绍 启动时优化 和 运行时优化。

5.1 启动时优化

  • 应用 v8-compile-cache 缓存编译代码
  • 优先加载外围性能,非核心性能动静加载
  • 应用多过程,多线程技术
  • 采纳 asar 打包:会放慢启动速度
  • 减少视觉过渡:loading + 骨架屏

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

应用 V8 缓存数据,为什么要这么做呢?

因为 electorn 应用 V8 引擎运行 js,V8 运行 js 时,须要先进行解析和编译,再执行代码。其中,解析和编译过程耗费工夫多,常常导致性能瓶颈。而 V8 缓存性能,能够将编译后的字节码缓存起来,省去下一次解析、编译的工夫。

次要应用 v8-compile-cache 来缓存编译的代码,做法很简略:在须要缓存的中央加一行

require('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 过程中进行解决。

至此,性能优化就介绍完了。

六、品质保障

品质保障的全流程措施如下图所示:

本章节次要介绍以下 3 个方面:

  1. 自动化测试
  2. 解体监控
  3. 解体治理

上面将会顺次介绍上述内容。

6.1 自动化测试

自动化测试是什么?

上图是做自动化测试一个残缺步骤,大家能够看图体会。

自动化测试次要分为 单元测试、集成测试、端到端测试,三者关系如下图所示:

个别状况下,作为软件工程师,咱们做到肯定的单元测试就能够了。而且从我目前教训来说,如果是写业务性质的我的项目,基本上不会编写测试相干的代码。自动化测试次要是用来编写库、框架、组件等须要作为独自个体提供给别人应用的。

electron 的测试工具举荐 vitest、spectron。具体用法参考官网文档即可,没什么特地的技巧。

6.2 解体监控

对于 GUI 软件,尤其桌面端软件来说,解体率十分重要,因而须要对解体进行监控。

解体监控原理如下图所示:

解体监控技巧

  • 渲染过程解体后,提醒用户从新加载
  • 通过 preload 对立初始化解体监控
  • 主过程、渲染过程通过 process.crash() 进行模仿解体
  • 对解体日志进行收集剖析

解体监控做好后,如果产生解体,该如何治理解体呢?

6.3 解体治理

解体治理难点:

  • 定位出错栈艰难:Native 谬误栈,无操作上下文
  • 调试门槛高:C++、IIdb/GDB
  • 运行环境简单:机器型号、零碎、其他软件

解体治理技巧:

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

七、平安

俗话说的好,平安大于天,保障 electron 利用的平安也是一项重要的事件,本章节将平安分为以下 5 个方面:

  1. 源码透露
  2. asar
  3. 源码爱护
  4. 利用平安
  5. 编码平安

上面将会顺次介绍上述内容。

7.1 源码透露

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

你只须要下载 asar 工具,而后对 asar 文件进行解压就能够失去外面的源码了,如下图所示:

通过图中操作即可看到语雀利用的源码。下面提到的 asar 是什么呢?

7.2 asar

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

asar 技术原理:

能够间接看 electron 源码,都是 ts 代码,容易浏览,源码如下图所示:

从图中能够看出,asar 的外围实现就是对 nodejs 的 fs 模块进行重写。

7.3 源码爱护

防止源码透露,依照从低到高的源码平安,能够分为以下水平

  1. asar
  2. 代码混同
  3. WebAssembly
  4. Language bindings

其中,Language bindings 是最高的源码安全措施,其实应用 C++ 或 Rust 代码来编写 electron 利用代码,通过将 C++ 或 Rust 代码编译成二进制代码后,破译的难度会变高。这里我说下如何应用 Rust 去编写 electron 利用代码。

计划:应用 napi-rs 作为工具去编写,如下图所示:

咱们采纳 pnpm-workspace 去治理 Rust 代码,应用 napi-rs,比方咱们写一个 sum 函数,rs 代码如下:

fn sum(a: f64, b: f64) -> f64 {a + b}

此时咱们加上 napi 装璜代码,如下所示:

use napi_derive::napi;

#[napi]
fn sum(a: f64, b: f64) -> f64 {a + b}

在通过 napi-cli 将上述代码编译成 node 能够调用的二进制代码。

编译后,在 electron 应用上述代码,如下所示:

import {sum as rsSum} from '@rebebuca/native'
// 输入 7
console.log(rsSum(2, 5))

napi-rs 的应用请浏览官网文档,地址是:https://napi.rs/(opens new window)

至此,language bindings 的论述就实现了。咱们通过这种形式,能够实现对重要性能的源码爱护。

7.4 利用平安

目前熟知的一个平安问题是克隆攻打,此问题的支流解决方案是将用户认证信息和利用设施指纹进行绑定,整体流程如如下图所示:

  • 利用设施指纹生成:能够用上文论述的 napi-rs 计划去实现
  • 用户认证信息和设施指纹绑定:应用服务端去实现

7.5 编码平安

次要有以下措施:

  • 罕用的 web 平安,比方防 xss、csrf
  • 设置 node 可执行环境
  • 窗体开启平安选项
  • 限度链接跳转

以上具体细节不再介绍,自行搜寻上述计划。除此之外,还有个官网举荐的最佳平安实际,有空能够看看,地址如下:https://www.electronjs.org/docs/latest/tutorial/security(opens new window)

至此,平安这块就介绍完了。

八、总结

本文介绍了咱们对桌面端技术的调研、确定技术选型,以及用 electron 开发过程中,总结的实践经验,如构建、性能优化、品质保障、平安等。心愿对读者在开发桌面利用过程中有所帮忙,文章不免有有余和谬误的中央,欢送读者在评论区交换。

正文完
 0