#一、业务背景随着公司业务的快速发展,商户客服也被纳入了我们的服务范围。商户客服工作台的定位是通过工具和数据为商户服务。一站式解决用户采购咨询需求。通过工具和运营策略帮助商家提升服务质量,让品牌商家有动力去管理好潜在客户,从而达到提升用户服务的目的。桌面应用的转型将是未来客服产品的方向。在已有Web端聊天系统的前提下,商家客服为什么要迁移桌面应用?首先,我们收到一些商家的客服反馈:用户就是上帝,我们非常重视用户的反馈,所以我们首先思考的是如何在web端解决这些问题。下面针对以上问题一一分析。我们可以在网络端解决它们吗?羊毛布?客服提问A:大部分客服桌面都没有安装音响设备。如果一个人没有音频,没有外接语音,我们可以强制他买播放器吗?那是绝对不可能的。如果是自营客服还是有一些解决办法的。如果有需要,公司可以统一采购,但是ToC显然不能强求什么,所以这个问题无解。客服B同学:这句话怎么理解?就像这样。在一个屏幕上,网页浏览器和其他应用程序(例如飞书)和其他PC应用程序层层叠叠。Web通知在浏览器级别。是的,提醒达不到顶层,浏览器的提醒确实有点弱(看不到提醒会影响客服的第一次调用,第一次调用会影响客服的表现。我们公司是对用户的服务效率要求比较严格),所以没有解决这个问题。客服同学C的问题:em。..好吧,你说的对,网页给商家客服的感觉就是我们平台有点落伍了。..基于上面的一些场景,想必大家对为什么要使用桌面应用程序有了一个初步的认识。我们先来一张图看看Web应用和桌面应用的区别。2、为什么选择Electron而不是其他应用开发框架来进行技术选型?2.1Electron架构介绍Electron的构成主要由以上三大模块组成,各司其职,让Electron具备了桌面应用的能力。Chromium:可以为Electron提供UI渲染能力,而且Chromium本身是跨平台的,不需要考虑代码兼容性。只要有了Chromium,就可以使用前端开发工程师熟知的三剑客JavaScript、HTML、CSS来开发页面。Node.js:Chromium不具备操作原生GUI的能力,因此Node.js只是对这种能力进行了补充。可以调用操作系统底层API,如path、fs、crypto模块,甚至可以集成C++。NativeAPIs:NativeAPI使Electron具备了跨平台和桌面的原生能力,比如有统一的原生界面、窗口、托盘等。2.2Electron和其他框架的区别下面是Electron和Native的对比图、QT、NW应用:Native(C++/C#/Objective-C)在原生体验、包大小、性能等方面是最佳选择,但开发门槛高,迭代速度慢。QT是一个基于C++的跨平台开发框架,具有广泛的跨平台应用(Mac、Windows、ios、Android、Linux、embedded)。众所周知的WPS就是用QT开发的。性能非常好,甚至可以媲美原生体验,但整体门槛还是比较高。Electron和NW.js是Web技术的代表。与之前选择Electron相比,Electron拥有非常活跃的社区,拥有102kstar。Atom、vscode等大型应用都是基于Electron开发的。与natvie相比,它的性能肯定更好。更糟糕的是,但总的来说,Electron是目前开发桌面应用程序的首选。值得一提的是Flutterdesktop,从渲染原理来看,flutterisskia自绘性能优于Electron,但问题仍然是稳定性和生态。由于Electron是nodejs+chromium,可以直接使用前端生态,所以不考虑Flutter桌面,但值得关注。除了以上,最重要的一点:Electron可以快速交付,业务层可以复用web系统的代码。只需要关注主流程,也可以满足业务需要的一些系统级功能。3.技术实现3.1项目架构首先介绍Electron框架中的两个重要概念,主进程和渲染进程。主进程:主要负责创建和管理BrowserWindow实例和应用程序事件。它可以执行注册全局快捷方式、创建系统菜单和对话框、响应自动更新事件等操作。ElectronAPI的一部分在主进程以及所有Node.js模块中可用。渲染进程:渲染进程负责运行应用程序的用户界面,在渲染进程中提供了所有DOMAPI、Node.jsAPI和ElectronAPI的子集。如上截图所示,打开Electron项目后,会出现多个进程。一个项目只有一个主要过程。创建窗口等系统事件写在主进程中,但可能有多个渲染进程。那么为什么会有多个渲染进程呢?Electron应用是Chromium内核,所以多进程架构也是从Chromium衍生出来的。Chromium将独立运行每个选项卡,任何选项卡崩溃都不会影响其他选项卡。因此,每个进程都运行在自己的空间中,由操作系统调度。如果一个进程触发死循环,它不会导致整个应用程序宕机。上面说的“迁移”两个字,是的,我们在开发桌面应用之前就有一个非常成熟的web端业务客服聊天系统,所以我们在开发桌面应用的时候,大部分时间关心的都是主进程。渲染过程并不需要真正关心。3.2主要流程功能模块3.2.1通信模块主要是调用Electron框架自身的API和通用方法的封装。Communicationfromthemainprocesstotherenderingprocess:Communicationfromtherenderingprocesstothemainprocess:有两种选择,一种是在主进程打开nodeIntegration:true后在渲染进程中使用window.require('Electron')一种通信相关的代码是在主进程写preload.js,在初始化窗口的时候引入通信的同步和异步问题【异步】渲染进程->发送->主进程【同步】渲染进程->发送->主流程3.2.2菜单模块主要调用Electron框架自身的API,满足快速展开菜单和自定义菜单的功能。但是商家客服项目并没有使用原生菜单,而是使用了自定义菜单。没有使用原生菜单的主要原因是:原生菜单样式比较死板,无法调整;自定义编写的菜单可以有你想要的各种功能,你可以控制它的显示。3.2.3Tray模块Tray是系统级的操作,所以在主进程中设置。在开始之前,需要注意,设置托盘必须在程序准备好之后。实现比较简单,代码如下:3.2.4异常处理模块主要调用Electron框架自身的API,结合Node.jsAPI,检测到系统异常后自动刷新并上报,异常渲染过程已经通过sls&arms处理,主进程的异常主要是Logging是通过Electron的crashReporterAPI来完成的。上面的提交参数有几点需要注意:submitURL是上传一个额外的对象,你可以定义post方式,它会和crashreport一起发送。只能正确发送字符串属性,不支持嵌套对象。3.3渲染流程功能模块渲染流程大部分代码与商户客服web端一致,很多只能迁移。3.3.1登录转换登录信息本地化。登录成功后,缓存账号信息。下次打开应用时,客服不需要重新输入,直接从缓存中获取即可。3.3.2静态资源在传统的web应用中,项目代码是部署到服务器上的。项目运行时,访问服务器的静态资源。现在版本发布过程使用CDN资源,都是通过网络获取。Electron提供打包到安装程序中的静态资源。安装时将项目文件同步安装到用户电脑上,使用户可以访问本地文件,减少请求占用资源,一定程度上改善因网速问题导致的静态资源问题。无法实时获取,页面空白。3.3.3数据存储Electron应用中的数据存储是通过Electron-store第三方库实现的。实现比较简单,如下:3.3.4渲染过程打包为什么要把渲染过程打包单独挑出来?Web项目迁移到应用渲染进程时,不能像Web应用那样直接打包。需要调整请求API代码,API前缀需要区分本地调试和应用环境:使用Electron将项目打包成离线应用。使用文件协议在本地读取静态资源。但是如果ajax请求使用的是相对路径,打包后会直接找到根目录,如下截图所示:所以打包时需要给ajax提供完整的url路径。#4.技术挑战在从0到1搭建客服桌面的过程中,我遇到了很多问题。虽然Electron社区很活跃,但是针对不同场景遇到的问题几乎没有对应的解决方案,所以很多都是在探索的过程中不断完善。这里我们主要关注发布构建过程和安全性,以及我们是如何解决的。4.1安全问题Electron客户端的安全问题也很重要。遇到过哪些安全问题以及我们是如何解决的,如下:渲染过程XSS:Electron实现的桌面软件渲染层原理其实是通过Chrome内核渲染也存在XSS注入的风险。例如:在html页面中,可以执行命令:打开当前操作系统的计算器。接入公司统一的XSS治理方案后,问题得到解决。用户认证信息泄露:商户客服桌面登录调用商户授权接口,APP网关有校验保证登录没有问题;本地缓存明文读取问题:本地数据泄露,如:indexDB、localStorage、sessionStorage等,我们主要对本地缓存信息进行加解密算法处理。没有绝对的安全。我们能做的就是尽可能的提高安全门槛。过程中,我们也积极与公司安全部门沟通,让他们在桌面端发布后排查安全漏洞。最终验证满足安全标准,满足发布条件。4.2发布与构建流程应用发布涉及渲染流程和主流程。渲染进程主要负责向主进程提供渲染包。主流程使用Electron-builder库对发布的包进行打包部署。前面说过,Electron的优势在于可以无缝集成web端的业务逻辑代码。这里,上图左边红色部分就是web端搭建的产品。我们会将这部分构建好的产品同步到主进程的app/render目录下,即渲染进程目录下,这样在应用打包的时候,就可以集成渲染进程的业务逻辑,不需要再维护两个网页端代码。该解决方案仍然存在许多缺陷。由于生产搭建环境需要window环境,所以暂时不支持远程打包。目前是在本地window机器上打印完整包,然后上传到CDN。商家客户服务从CDN加载更新。包替换本地安装文件,实现软件的本地安装。4.3应用更新问题应用开发离不开“更新”这个话题。比如飞书应用会时不时弹出更新窗口,让你选择是否更新。商家客服推广桌面应用后,也出现了更新问题。随着业务的快速发展,如何更好地将业务需求与商户同步是商户客服桌面应用面临的最大挑战。4.3.1全面更新4.3.1.1手动下载安装最基本的更新方式,主要思路是打开app时访问远程json文件(其他时候我们的业务主要是打开app时),内容如下该文件包含最新版本的版本号、更新内容等信息。远程版本号将与本地应用程序版本号进行比较。如果版本号不一致,会询问用户是否更新。如果需要更新,会下载到本地。用户可以手动点击安装。不推荐这种更新方式。如果你的应用每年更新一次,ok,你可以这样做。4.3.2增量更新在网速快的情况下,完全更新和增量更新几乎没有区别。但是当网速慢的时候,两者的差距就会拉大,用户体验不是很好。我们不能想当然地认为所有用户的网速都很好。这是不现实的,所以无论是PC应用还是移动应用,大多数情况下都需要增量更新。下表是不同网速下的下载时间对比:4.3.2.1electron-updater下面介绍如何在商户的客服应用(windows应用)中实现增量更新功能。更新在大类上区分全量更新和增量更新,在每个小类上区分强更新和弱更新(业务上有区别,底层实现没有区别)。简单来说,强更新就是用户必须更新,不更新就不能使用系统功能。弱更新是指用户在需要的时候触发应用的更新,完全由用户自己选择。在更新过程中,electron-updater作用于“更新应用”节点,主要依靠blockmap文件新旧版本的对比实现增量更新。下面截图是electron-builder打包的release包。每个包都会有一个相应的块映射文件。electron-updater更新实现的主要过程:blockmap文件产生:1.使用7z压缩安装包2.读取安装包头3.计算每个文件的offset和end得到对应的hash产生blockmap使用blockmap文件:1.在云端下载blockmap文件,与本地blockmap文件进行对比。从上面的截图可以看出blockmap文件很小,所以下载不会影响应用性能。2.2文件替换另一种增量更新方式是文件替换,只更新需要更新的模块。该方法只更新需要渲染的资源。大多数情况下,主进程的资源是不需要改动的,所以下载的资源会比较少,更新比较快,因为是在线热更新,更新完成后不需要重启软件,刷新页面重新加载资源即可。其实我之前觉得这种思路挺好的,看下面的流程图就可以实现,也符合商家客服桌面应用的需求。不过后来发现其实忽略了以下两点:替换用户本地文件本身就有权限问题。例如Windows用户安装到C盘,写入文件受管理员权限限制;文件被占用。众所周知,当文件夹中有正在使用的文件时,文件夹删除会失败。因此,需要退出应用程序,以免在覆盖原文件的同时占用,所以这种方法不是很可靠。5、遇到的问题Electron的硬件加速功能在win7或Linux系统上容易黑屏或卡顿。解决方法:判断是否是win7及以下系统,如果是app.disableHardwareAcceleration(),则关闭当前应用的硬件加速。UncaughtReferenceError:requireisnotdefined,在渲染过程中尝试使用node时会出现这个错误,也不是不可以,只要开启主进程的nodeIntegration:true即可,但不推荐,有安全问题。解决办法:注意:你的临时目录中可能有一个或两个(大的)陈旧的临时文件(一般这种情况只发生在Windows9x)这种情况是长时间打包后无法生成完整的包出现了。解决方法:当时因为我没有删除原来的包,所以放包文件的C盘已经满了。..所以只要删除一些缓存,nsis打包很有可能和磁盘有关。下载npm包特别慢解决办法:yarn安装;electron相关包优先使用淘宝镜像安装;使用公司镜像安装公司内部包。6.总结一路发展下来,感慨颇多。作为公司的第一个Electron应用,无论是在开发、打包还是部署上,我都遇到了一些挑战。网上没有更详细的文档。在外面做。具体的方案我就不分享了,但是即使遇到这些问题,也不能否认Electron是目前最适合我们业务目标和开发资源的框架。目前已有在线稳定版,正在逐步推广至所有商户客服。接下来需要完善开发流程,还有很多技术难点需要攻克,商户客服工作台的应用会越来越完善。文/单
