关于前端:基于Electron开发桌面应用的技术实践

2次阅读

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

哈骑士是哈啰的一款终端平安利用,本文次要介绍咱们在做新版哈骑士桌面端时的一些技术架构思考和实际,分享咱们积淀的一些桌面端利用的解决方案和教训。

为什么抉择 Electron

前端开发者入门快

Electron 是一个应用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。嵌入 Chromium 和 Node.js 到 二进制的 Electron 容许您放弃一个 JavaScript 代码代码库并创立 在 Windows 上运行的跨平台利用 macOS 和 Linux——不须要本地开发教训,有了它,前端开发者就能够应用前端开发技术来开发桌面利用了。

反对跨端 & 开发效率高

如上图所示:

  • Native(C++/C#/Objective-C)不论从原生体验、包的体积、性能方面来说都是最佳的抉择,然而开发门槛高、迭代速度慢。
  • QT 是基于 C ++ 的跨平台开发框架,跨平台利用非常宽泛(Mac、Windows、ios、Android、Linux、嵌入式),家喻户晓的 WPS 就是用 QT 开发的。性能很好,甚至于能够媲美原生的体验,然而整体门槛还是比拟高的。
  • NW 也是一个跨平台的框架,然而其社区以及解决方案绝对于 Electron 来说并不是那么弱小,而且所有的非 javascript 编写的模块都须要从新用 nw-gyp 从新编译,绝对于 Electron 来说,不是那么灵便。
  • Tauri 也是一个十分火爆的跨平台的桌面端框架,绝对于 Electron 来说还不是那么成熟,生态方面也略显青涩,兼容性问题有待考据。

作为一个跨平台的桌面利用开发框架,Electron 的迷人之处在于,它是建设在 Chromium 和 Node.js 之上的,二位分工明确,一个负责界面,一个负责背地的逻辑。尽管零碎间还是会有很大的差别,须要相应地做一些额定解决,使得打包出的利用在不同零碎下都能失常运行,但相比于 80% 都能齐全复用的代码,这些工夫和老本都是能够疏忽的,开发效率间接翻倍,如果你开发一个不须要太关注底层的桌面端利用,根本不须要做底层的抹平逻辑。

另外,Electron 是基于 Node.js 的,这就意味着,Node 这个大生态下的模块,Electron 都能够用。同时,跨平台也让 Electron 可同时开发 Web 利用和桌面利用,无论是 UI,还是代码,很多资源都能够共享,大幅缩小了开发者的工作量。

生态凋敝 & 案例成熟

Electron 生态确实很弱小,各种库和工具包都为你构建一个桌面端利用提供了很多计划。

当然,不止如此,当初用 Electron 做桌面端的案例也十分成熟了。上图曾经阐明了 Electron 利用是有多宽泛了,这其中不乏赫赫有名、如雷贯耳的利用,例如 Postman、Skype、VScode 等。而且我敢打赌,各位看官的电脑上肯定装置过用 Electron 开发的利用,如果你用的是 Mac 电脑,请在命令行运行上面的命令来检测本地采纳 Electron 技术开发的桌面软件:

for app in /Applications/*; do;[-d $app/Contents/Frameworks/Electron\ Framework.framework] && echo $app; done

Electron 生态开发技术选型

脚手架选型

对于脚手架的抉择,其实也很多。

官网提供的有 Electron Forge,Electron Fiddle,electron-quick-start,其实如果你的利用不简单,能够用官网的脚手架生成一个疾速上手的模版,而后就能够欢快地开发了。

当然也有一些开源的脚手架,比方 electron-vue 或 vue-cli-plugin-electron-builder 之类的,也能够让你疾速的生成一个固定的模版,而后往里面填充你的内容。

集体认为,官网的脚手架工具能够用来尝鲜,学习应用,electron-vue 这类工具,如果是在一个企业级的我的项目中应用,后期会给你带来便当,然而前期扩大不会太敌对,另外就是他们是基于 webpack 构建的工具,在日常的开发和应用中会感觉编译得不够快(绝对于 Vite)。

另外就是如果你想本人实现一个我的项目脚手架(我的项目框架),齐全能够凭借本人的教训或者参考开源我的项目的架构本人来实现一个脚手架,一来是为了更加理解 Electron 的构建原理,二来是能够搭建出适宜本人格调我的项目的脚手架,前期利于扩大和丰盛。

所以咱们脚手架的选型就是本人来造一个 Electron 的我的项目架构,从 package.json 开始,用 Vite+Electron+React 构建一个 Electron 我的项目。

网络模块选型

Electron 发送 HTTP 申请的计划有很多。

第一种就是渲染过程和主过程别离用相应的申请 HTTP 申请工具来进行网络申请,比方渲染过程能够应用 fetch, 主过程用 net 模块。这种计划的长处就是能够把渲染过程和主过程的申请离开,分工明确,而且调试也不便,渲染过程能够间接看 network;毛病就是,如果要对申请进行对立封装的话,比拟麻烦。

第二种就是所有的申请对立封装,如果你都应用 net 模块或者其余的申请工具包对申请进行对立的封装,而后主过程间接应用,渲染过程调用对立的桥接办法。这种计划就是齐全能够对立申请封装,然而如果想调试的申请的话,不不便,须要在主过程来日志信息。

第三种就是,间接 axios 间接一把梭,它既反对 node 环境,也反对浏览器环境。这种计划十分不便,你就依照之前封装 Web 利用申请的思路去封装本人的申请模块就行,不过须要留神跨域问题。

对于下面的几种计划,各有各的优缺点,能够依据本人的场景需要来决定应用哪种计划。咱们抉择了 axios 来设计网络申请模块。

本地数据库选型

Electron 的本地数据存储形式也有很多种,能够间接读写文件,也能够用相干的库,不便数据管理。一些库的比照,详情:https://www.npmtrends.com/electron-store-vs-lokijs-vs-lowdb-v…。

综合来看 lowdb 更胜一筹,所以抉择 lowdb 做本地数据库,十分好的一点是它反对同步,不用放心数据没有写入就进行了下一步须要本地数据的业务操作。

日志工具选型

日志工具对 Electron 的开发也是尤为重要的,能够给你定位到一些表层无奈定位的问题,所以一款好的日志工具对开发是十分有帮忙的。

比拟常见的日志工具就是 electron-log 和 log4js-node,这两款日志工具我都有用过。能够看下 npm 的排行,这里把 express-winston 和 logging 也加上看一下,详情:https://npmtrends.com/electron-log-vs-express-winston-vs-log4…。

这里简略说一下 electron-log 和 log4js-node 的比拟,两者上手都比较简单,log4js-node 裸露的 API 十分多,electron-log 就稍显逊色了,另外最直观的感触就是,electron-log 的日志文件门路不好找,临时没发现自定义日志门路的办法,log4js-node 有相应的办法,而且你能够自定义各种文件类型。依据应用体验,感觉 log4js-node 更好,举荐 log4js-node。

构建工具选型

三种构建工具 electron-builder, electron-forge, electron-packager 比照一下。

从这个排行来看 electron-builder 确实很强,electron-forge 最近又更新大的版本,不过没有尝鲜,我在 electron-builder 上倒是踩了不少坑,能够分享给大家。所以我在开发的时候抉择的构建打包工具是 electron-builder,它把整套解决方案都集成了,包含打包、更新、签名、散发,根本的钩子和配置都有相应的裸露。

外围架构实现

架构概览

咱们整个框架是基于 Eletcorn + Vite 构建的,在底层依赖的平安能力和存储模块的基础设施之上设计了一层根底框架,实现构建打包,架构分层的设计,而后给整个桌面利用提供一些利用治理能力和 GUI 治理相干的能力,最上层就是为了一些业务场景提供的一些利用能力,包含外围的几个利用和次要的策略引擎利用(终端策略和合规策略)。

开发构建 Electron 是多过程架构的体系,所以咱们在开发构建的时候就是构建多个过程来实现咱们的利用。外围思路是通过 Vite 构建三个过程:渲染过程,工作过程,主过程,而后最初将三个过程交融起来,就造成了一个利用。外围代码如下:

几个留神点:

  • 咱们这里利用了 writeBundle,就是等 chunk 都写入文件后,再启动 Electron 过程。
  • 这里没有利用 Electron 的命令启动,而是通过 Node.js 的 child_process 模块的 spawn 办法启动 Electron 子过程,次要是因为咱们须要依赖开发环境的渲染过程。
  • 另外就是 config/vite/main.js 中须要对 rollupOptions 的 external 进行 electron 的配置,把导入包转成内部依赖,不然在启动 Electron 会找不到 Electron 的门路。
  • 在 createMainServer 中咱们注入了全局可应用的变量,以便 Electorn 加载页面的时候能够应用这些变量。

架构分层

因为须要跨端开发,Mac 和 Windows 有些底层模块的实现还是有不一样的中央,所以咱们在开发设计的时候将代码进行了分层设计,这样至上而下的调用在下层看来是一样的,所以咱们须要磨平端上底层的差别,现阶段咱们底层模块的实现是通过目录来严格辨别的,这样在开发一个底层的性能的时候就能够做到各段互相不影响。

打包降级

桌面客户端相当于传统的 Web 利用在打包和更新这一块还是有十分大的不同的,传统的 web 利用简直不必所谓的降级,浏览器刷新页面即可,然而桌面客户端就须要残缺的给用户一个能够立刻执行的装置应用程序,而且还要可继续迭代和更新,所以在打包降级这一块,咱们也是踩了不少坑。

1. 对于打包
打包其实 Electron 的生态也是十分成熟的,如下面提到的构建技术选型,咱们抉择的是 electron-builder,它提供了一套打包构建降级的流程,裸露了很多 API,傻瓜式的配置就根本能够让你实现一个利用的打包了,惟一麻烦的就是签名和认证利用。

在 Windows 端咱们应用 pfx 格局的证书进行认证,在进行打包的时候会和证书客户端软件交互,实现各个文件的签名,这样用户应用客户端的时候就是签名过的软件了。

在 Mac 端咱们须要应用苹果认证的开发者证书进行签名和认证,配置相应的 identity 后,构建打包的时候会间接跟你本地的证书进行交互,而后对文件进行签名,以后咱们还须要让利用能够不用严格应用 MAP_JIT 标识也能写入和运行内存内容。所以须要退出 entitlements 和 entitlementsInherit。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
  </dict>
</plist>

到这一步其实 Mac 端的软件签名就实现了,然而如果利用想 App Store 上架的话还须要对利用进行公证。公证次要是应用 electron-notarize 来进行公证,启用 afterSign 即可,afterSign: ‘./script/notarize.js’,

上面的 Apple ID 就是你的开发者账号,appleIdPassword 须要生成一个专用的利用明码,不要应用你原本的 Apple ID 明码。

const {notarize} = require("electron-notarize");

exports.default = async function notarizing(context) {const { electronPlatformName, appOutDir} = context;
  if (electronPlatformName !== "darwin") {return;}

  const appName = context.packager.appInfo.productFilename;

  console.log(\` 公证中...\`)

  return await notarize({
    appBundleId: "mac.hellobike.knight",
    appPath: \`${appOutDir}/${appName}.app\`,1
    appleId: "XXXXX@outlook.com",
    appleIdPassword: "XXXXX",
  });
};

notarize 会依据你的配置去校验你的利用是否能够公证胜利,公证的时候会和苹果的服务器进行通信,所以须要放弃网络不要断开,胜利或者失败之后都会发送相应的邮件到你的开发者邮箱外面。到这里打包的外围工作就做完了,如果你须要其余个性化配置,参考 electron-builder 官网的文档即可。

2. 对于降级
降级咱们在 Mac 和 Windows 上的实现各有不同,因为相比于传统的软件,咱们哈骑士会始终保活在用户的过程中,所以在更新降级的时候也会突破本来 Electron 降级的机制。

在 Windows 上其实还好,能够利用 electron-updater 自身的生命周期来实现下载,更新,重启利用,因为 Windows 的保活是用另外的服务来实现的,所以并不会对整个更新周期产生破坏性的影响。

然而 Mac 端的保活实现是突破了 electron-updater 自身的生命周期的,探索其源码会发现 Electron 本人的降级服务其实也是一个保活的应用服务,所以在降级之前须要将其 Kill 后能力实现哈骑士本人自身的更新逻辑,另外就是文件占用和锁定的问题,为此咱们自研了一套更新脚本程序联合 electron-updater 的下载更新的能力实现了 Mac 端软件的降级。

外围能力积淀

根底能力

咱们在做哈骑士客户端的时候,也积淀了一些与业务无耦合的组件和工具类,这些组件和工具在桌面端利用的场景都比拟通用。

  • 本地数据库治理
    本地数据存储是业务场景中随处可见的重要性能。为此,咱们封装了罕用的增删改查数据库的能力,并提供给各个过程应用,以实现数据长久化存储。
  • 底层桥接
    底层桥接是解决 Electron 和 Node 无奈笼罩所有利用场景的必要伎俩。咱们在桥接层封装了三种桥接模式,别离为渲染过程调用的 jsBridge 能力、主过程调用 dll 和 dylib 插件的能力,以及桥接 rust 程序的能力。这三种模式基本上能够解决所有技术瓶颈。
  • 客户端申请
    客户端申请模块也是至关重要的。咱们将其封装成了通用的 http 申请库,反对主过程、渲染过程和工作过程的调用,以抹平下层调用的差异性。
  • 工作治理
    因为业务场景和客户端的特殊性,咱们常常须要进行本地工作治理。因而,咱们将工作治理模块封装成了通用的工具类,以反对对工作的注册、启动、进行和销毁等各项生命周期的治理。

利用能力

在下面这些根底能力的组合利用下,咱们造成了一个弱小的策略引擎利用。

该策略引擎利用实现了端上任务调度和散发性能。首先接管后盾配置的策略信息,而后生成对应的工作,并散发到各个子工作核心以执行对应的策略。最初,将策略执行状况报告给服务端。

总结

Electron 在哈骑士的利用十分胜利,尽管在应用过程中遇到了一些问题,但不可否认它是目前最适宜咱们业务指标和开发资源的框架。应用 Electron 使需要交付效率失去了很大的晋升。咱们也将继续关注性能和稳定性的优化、桌面端全链路日志的欠缺以及增量更新降级能力等方面的改良。

(本文作者:徐涛焘)

正文完
 0