哈骑士是哈啰的一款终端平安利用,本文次要介绍咱们在做新版哈骑士桌面端时的一些技术架构思考和实际,分享咱们积淀的一些桌面端利用的解决方案和教训。
为什么抉择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使需要交付效率失去了很大的晋升。咱们也将继续关注性能和稳定性的优化、桌面端全链路日志的欠缺以及增量更新降级能力等方面的改良。
(本文作者:徐涛焘)