如何打造高质量的 Electron 应用?

2,244 阅读12分钟

编者按:Electron 可以说是前端工程师开发桌面应用的首选框架之一,但是对于前端工程师而言,其带来的复杂度也更高,我们面对的质量和稳定性挑战也会更大。本次分享中,蚂蚁集团语雀前端工程师叙南将结合语雀桌面端的发展历程以及其中面临的挑战,聊一聊如何通过工程化手段来保障 Electron 应用的质量与稳定性,欢迎享用。

演讲视频地址:player.bilibili.com/player.html…

各位同学大家好,我是叙南,现在负责语雀桌面端的研发工作。今天想跟大家分享一下,语雀在桌面端上的一些质量实践。

今天我的分享主要分为三个部分。第一,语雀是什么。第二,我们为什么会选择 Electron 去开发桌面应用。第三就是我们今天的主要内容,语雀桌面端做的一些质量和稳定性的方案。

语雀是什么

语雀是什么?一句话来说,语雀是一款专业的云端知识库。我们给大家提供好用的知识管理和安静愉悦的团队协同的能力。

桌面端在语雀中扮演的角色又是什么?我们在语雀的发展过程中发现,语雀上其实有非常多的个人知识管理的用户。我们希望通过端的系统能力来给这些用户提供更好用的个人笔记工具,也给他们带来更纯净的创作体验。

为什么选择 Electron

既然提到桌面端端,我们就要考虑如何去做技术选型,用什么框架去开发了。

那为什么我会选择 Electron 呢?首先先讲一下团队的技术背景。语雀还是一个面向 Web 技术的全栈团队。我们的编辑器和很多底层的业务能力都是基于 JS 语言开发的。其次,在那段时间我们还是一个非常小的一个创业团队。我们有非常强烈的跨端诉求,希望可以一套代码跑在多端上。最后我们希望我们选择的框架有成熟的产品为我们背书,我们不需要参与它的底层开发。

这时候我们会发现 Electron 是一个非常好的选择。首先它是基于 Chromium 内核的,我们可以用 JS 去开发我们的页面;其次,它可以通过 Node.js 去做网络和文件操作,大部分的前端工程师对这块还是非常熟悉的;他也提供了非常丰富的系统交互 API。最后,大家都知道包括 VS Code 等国内外非常知名的产品都是基于 Electron 构建的。

语雀桌面端的整体架构大概就是这样:首先还是基于云服务去搭建我们的基础设施;其次,基于之上我们会构建很多跨端能力;再往上一层就是我们的桌面应用的能力,比如说我们的 GUI 管理、发布工具、研发支撑等等。

但今天的重点还是去分享前端工程师去开发 Electron 应用时需要面对的一些质量和稳定性的挑战是什么?首先大家都知道开发端应用有一个很重要特点,就是它的发布频率更低,我们又有非常多的 Web 业务要集成,对代码质量要求会更高;其次,要面对渲染进程和主进程的交互,以及非常多的系统交互,排查问题的链路更长;最后,因为包是在用户的系统上,我们也会面临更多的安全性要求。

质量和稳定性探索

那我们怎么去解决这些问题?首先先看一下语雀的研发流程。其中开发同学会比较关注的是研发、测试、发布和监控阶段,语雀和很多团队不一样,我们是没有专职的测试团队的。因此语雀的开发工程师就是语雀质量和稳定性的一号位。

我们在这些阶段主要要面临的问题是什么呢?比如说研发阶段,我们更关注的是代码质量;在测试阶段我们更关注我们这一次开发的功能在集成测试时的覆盖度够不够;发布阶段则是我们包的触达率和下发的稳定性;最后就是线上的问题发现能力。

针对这些不同的阶段,我们的工程的手段又有哪些?比如说应对代码质量,我们可能会通过单元测试;测试覆盖度可能会在测试过程中提供一个增量的覆盖率;应对触达率和稳定性,就有动态更新和灰度能力,后面我会基于一些具体的场景来讲一下。

单元测试

说单元测试。提到单元测试大家的第一反应可能就是编码成本过高,可能写二十行代码,要写四十行的单测;第二就是效果不持续对吧,有的同学坚持一段时间会发现单测并没有带来很大的效果,就慢慢放弃了;最后就是大家的风格各异,可维护性比较差。

那我们怎么去解决这个问题?首先怎么尽可能降低我们编写单测的成本。第一个就是针对我们的业务模型提供非常多的数据构造函数,可以快速的初始化我们的业务数据。

第二就是我们知道 UI 的测试非常依赖用户操作,比如说你模拟用户点击 Button,然后引发 state 变化。我们会提供一个组件渲染的初始化能力。大家可以快速构造 props,这样直接构造出一个中态来去验证 UI 的一致性,而不需要复杂的用户操作。

第三,为了让单测更内聚。我们支持 mock 所有的系统依赖。比如 IPC 调用、网络调用等,这样就只需要关注本地单测的执行结果。

最后就是基于 Jest 提供的丰富的断言能力,去做终态的判断。通过这几个步骤我们基本上就逐步的将单测标准化。大家构造数据,然后模拟整个 UI 的渲染,最后做 UI 断言;这样可以保证整个团队的单测成本会逐步的降低。其次,大家的代码风格会统一。

下一步就是如何保障我们的单测效果。首先就是我们在代码提交之后的 CI 环节提供了覆盖率卡点以及测试报告。所有人都可以看到单测的覆盖度情况。

其次,针对一些单测本身覆盖率就不高的模块,我们提供了本次提交的增量覆盖率结果,来帮助大家发现缺口,逐步提高覆盖率。

集成测试覆盖率

为了提升软件质量,靠单测是远远不够的。我们都知道,单测的覆盖率高,不代表我们集成测试没问题,那我们怎么去在开发做集成测试的时候,尽可能的覆盖我们这次提交的代码呢?

既然提到覆盖率,我们还是回去看一下单侧的覆盖率是怎么做的。Istanbul 的方案其实是一个非常标准的方案。它对代码做 AST 解析,然后生成一部分插桩的代码,它会标注哪一段代码是 state,哪一段是function,执行之后会对它进行插桩。执行完成后就会有覆盖率结果。

基于它的思路,我们就尝试在测试包集成 Istanbul,在测试过程中生成插桩代码,这样在执行测试用例进行集成测试的过程中,就会拿到本次测试的代码覆盖率,这已经有点接近我们最后想要的增量覆盖率的结果了。

我们就知道 Git Diff 是可以拿到行列信息的,既然我们有了测试覆盖率,就可以拿运行时的覆盖率加 Git Diff 的行列信息去做对比,这样就可以拿到执行时的覆盖率。

一张简单的图给大家看一下(拿一个demo,我们真实的代码就不看了)。首先可以看到,置灰的部分就是历史代码,高亮的部分就是我们这一次提交的增量代码。其中标红的就是这次增量代码在执行过程中没有覆盖的地方。我们在集成测试的过程中,就可以通过这样一个覆盖率报告来大概评估,我这次测试有哪些功能没测到。是否要继续进行集成测试。

我们代码质量保证基本上就是以上两个方面。通过单测去保障我们的编码质量,通过集成测试覆盖率去保障我们的测试过程中的覆盖度。

稳定性思考

前面是代码质量部分,后面就是我们怎么应对线上的质量和端的稳定性,我认为桌面端的稳定性可以把它分为四个方面。

  1. 包安全,一部分是要面对的 Web安全,比如 XSS 漏洞;其次我们下发的包怎么防篡改,用户拿到的必须是我们提供的包,最后就是对安装包做的一些公证和签名,这是苹果和 Windows 的一些系统安全要求。
  2. 数据安全能力,因为我们做了非常多的离线。怎么去做本地的数据存储,离线数据怎么防丢,用户不要在离线切到在线的时候把之前的内容丢失了,这是我们的底线。
  3. 应用更新能力,比如动态更新,尽可能减少用户的更新次数,怎么去做灰度,怎么去回滚。
  4. 日志和监控,我们怎么去通过日志发现异常,帮助我们尽快去定位问题。

因为时间的问题,今天我们只挑一块,讲一下我们的日志监控以及全链路日志是怎么做的。

全链路日志

我们在做端日志的时候,主要的的诉求是什么?

  1. 因为端的操作链路会很长,涉及到端上的进程通信和本地的数据库操作等,我们要尽可能的收集操作日志,并且保证可以全链路追踪。
  2. 安全性要求会更高,要避免敏感数据的泄露。

在端上我们的日采集主要做哪些事呢?

  1. 从渲染进程发起请求之后,第一件事就是会基于时间戳和设备信息生成 Trace ID,帮助我们去做日志的链路追踪。
  2. 请求到了主进程,我们会做数据库操作、网络请求,还有一些业务操作,这些都会输出日志。这里一个非常重要的事情就是要对日志做脱敏,因为我们的日志是会落盘的。所以用户的文档内容、标题等敏感信息我们都不会存储。
  3. 有了这些日志之后,我们会做两个操作。第一个就是日志输出,像数据库操作会非常多,我们进行压缩和整合之后做批量上报,来减轻我们服务端的压力。其次就是因为有一些离线的情况,所以我们也会做一些本地存储。本地存储参考了服务端的日志模型,按类型区分,按日期进行滚动,方便我们追溯

除了端的一些日常报,我们还有端到端的整个日志链路。桌面端上报之后,首先是发到我们语雀云服务的服务端,然后我们会在服务端上打一条日志,之后基于阿里云的 SLS 去做分析和整合。比如在 SLS 我们会做一些线上分析大盘以及报警。对于一些需要长期分析的数据,会通过 ODPS 存储去做长期分析。

我们前端比较关注的日志类型有哪些呢?一般常见的有 unhandlerejection和 TypeError。语雀还有一点比较特殊,因为涉及到编辑器,我们会对编辑器和主应用做 Webview 隔离,我们会非常关注编辑器的 Crash的情况,所以会基于 React 的 componentDidCatch 去做编译器的 Crash 上报。

上报之后就可以在线上的 SLS 上看到报错信息了,定期去做一些分析。

还有就是一些业务情况的分析,每次发版的时候会关注核心数据,比如说非常核心的文档加载失败,文档保存失败这些情况。

未来探索

以上就是我们今天的主要内容。未来我们也在做一些自动化的 UI 测试的探索,来降低我们的应用测试回归成本。

这里有一个视频是我们在做的一个尝试。首先和很多团队的想法不一样,我们认为端测试需要有一个更稳定的用户运行时环境,所以是尝试在测试包去进行脚本的植入,来确保真实环境的执行。第二,我们会做一些网络的 Mock,去模拟弱网、断网情况下编辑保存的特殊情况。最后我们也会有一些测试机去做一些长期的定时运行,来帮助我们去洞察性能情况。比如用户长时间使用会不会有内存泄露以及 CPU 异常的情况。

总结

最后想跟大家分享一下在做桌面应用的这段时间里,我对质量和稳定性的一些看法。

  • 开发才是质量的一号位,我们可以通过工程的手段尽可能的提早避免一些质量问题。
  • 质量是渐进增强、重在坚持的事情,不管是单测还是日志与监控,它都是一个长期投资,很难一步到位就有收益
  • 方案和实际业务场景要做一些结合,质量是没有银弹的,大家需要在自己的业务和团队情况中去摸索更适合自己业务的一个质量方向。

我今天分享到此结束,感谢大家收看~