浅谈 Electron.js 中的架构设计

浅谈 Electron.js 中的架构设计

原文:Advanced Electron.js architecture – LogRocket Blog

不久前,我开始从事一个名为Taggr的附带项目,这是一个完全离线的交互式照片探索应用程序。开发Taggr要求我从应用程序复杂性的最低级别开始,尝试多种架构方法并探索每种局限性。

在本文中,我们将讨论使用 electron 构建桌面应用程序的不同构建方法之间的权衡。我们将分析每个不同方法的缺点,并介绍旨在应对它们的架构。

本文中介绍的蓝图是持续努力的结果,以找到一种方法,使我(一个独立开发人员)通过利用标准网络工具来管理应用程序的复杂性并满足性能要求。让我们开始吧!

注意: 你可以关注这个 GitHub repository.

Electron 简介

在过去的几年中,在浏览器领域内的JavaScript使用情况大大增加,在很大程度上是借助库和框架(例如React,Vue和Angular)的帮助。同样,JavaScript已经不仅仅局限于在浏览器中使用,而是可以在其他地方使用,比如Node.js、Deno和React Native。

Electron.js就是这些框架之一。自2013年发行以来,Electron 已成长为构建跨平台桌面应用程序最常用的框架之一。VSCode,Slack,Twitch和许多其他流行的桌面应用程序是使用Electron 构建的。

Electron 的工作原理

Electron 将 Chromium 和 Node.js 嵌入其二进制中,使 Web 开发人员能够在不编写原生桌面应用代码的情况下编写桌面应用程序。Electron 实现了一个与 Chromium 浏览器类似的由主进程和渲染进程组成的多进程模型。

每个应用程序的窗口都是一个渲染进程。每个窗口之间是相互隔离的执行上下文,主进程程负责应用程序生命周期管理,窗口管理或渲染进程管理,以及一些 native API(例如系统菜单,通知和托盘图标)等。

每个应用程序由一个主进程和一个可变数量的渲染进程组成。渲染进程可用于JavaScript代码执行,也可以在没有UI的情况下存在。

浅谈 Electron.js 中的架构设计

注意:Electron 不是构建跨平台桌面应用的唯一选择。其他替代方案也提供了更少的资源消耗和更小的可执行文件,但没有活跃的共享社区,学习资源或者得到像 Electron 一样的广泛传播和使用。

如何开始使用 Electron

如果您还不熟悉 Electron,它很容易上手,因为Node.js和JavaScript的知识是可以互通的。

Electron 抽象出原生应用层和熟悉的js语言,减少了推广和开发成本。从本质上讲,Electron 在桌面应用程序开发中所做的工作类似于 React Native 对移动开发的开发。

Electron 还可以管理构建和部署,以及应用程序更新,从而易于将跨平台应用程序保持同步的更新。您可以通过运行时加载远程资源来实现自动更新。

但是,Electron 带来了一些便利和好处,同时在权衡利弊中,也带来了一些不可避免的问题。Electron 开发框架默认包含了Chromium 和 Node.js环境。导致 Electron 应用比使用本地开发会消耗更多的资源。因此,一些人对 Electron 的性能和资源消耗表示担忧。

此外,由于基础体系结构,复杂的 Electron 应用程序会对应用程序的性能和开发人员来带一些挑战。我们通过三个不同的应用程序示例来进行深入分析。

特定应用程序的权衡

让我们研究具有不同复杂性的三个应用程序示例的高级结构设计。请注意,我们的目的并不是要详尽,而是旨在分析您可以使用 Electron 构建的什么样(能力边界)的应用。

低复杂性应用程序

让我们从一个低复杂的应用程序开始。就我们的示例而言,我们将将网页包装为桌面应用程序。示例可能包括即时消息传递应用程序,数据分析仪表板和在线流应用程序。

许多企业提供基于成熟的 Web 的应用程序的桌面版本,使我们的使用情况成为常见的用例。我们将使用 Electron 运行在 chrome 上的应用程序,消除不必要的差异,并提供统一的UI,而不是不同的浏览器有不同表现。

主要特点:

  • Web应用程序和桌面应用程序使用同一套代码
  • 当有新的功能或修复需要应用程序更新时,这些更新将同时应用于网页应用程序和桌面应用程序。
  • 桌面应用程序将加载与Web应用程序相同的资源,并将其渲染到Chromium中
  • 后端接口(如果适用)将保持不变
  • 桌面和 Web 应用程序都以相同的方式访问后端
  • 像 WebWorkers 和 WebGL这样依赖于浏览器支持的特性,在不同的平台上都可以正常工作,不需要进行任何更改。
  • 使用标准的Web开发工具

架构设计

作为示例架构,我们将使用桌面应用程序进行Telegram Chat Web应用程序。Electron 将成为当现有Web应用程序的容器,而无需对后端进行任何更改。
浅谈 Electron.js 中的架构设计
对于这种类型的应用程序,很容易更改为 Electron 应用!Web App代码库级别不需要更改。

中复杂性应用程序

像 Spotify 这样的音乐流应用程序,它是使用本地缓存提供脱机流支持的,是一个典型具有中等复杂性的应用程序的示例。桌面应用程序可以使用 Electron 来构建本地缓存层。

与低复杂性应用相似,中等复杂的应用程序也可以补充网络应用程序。主要区别是提供离线支持的能力。因此,这些应用在概念上与脱机支持的渐进式Web应用程序(PWA)相似。

主要特点:

  • 大多数代码可以在Web和桌面应用之间共享(即在UI层中)
  • 桌面应用程序将具有本地缓存实现,该缓存实现将拦截后端请求,设置缓存并在离线时提供缓存结果
  • 我们需要使用高级 Electron API检查桌面应用程序是在线还是离线应用程序
  • 更新不一定在网络和桌面之间共享。桌面将在离线情况下从静态文件加载UI,并使用缓存数据创建自定义请求层
  • 您可以利用标准的Web开发工具,除了必须为 Electron 开发调整的自定义请求模块外。

架构设计

想象我们的流媒体应用程序播放了当天的歌曲。如果当前没有互联网连接,它将提供可用的缓存歌曲。
浅谈 Electron.js 中的架构设计
如上面的架构中描述的那样,将从本地缓存而不是 CDN 提供UI层数据,并且必须自定义请求层以支持缓存数据。尽管示例相对简单,但代码共享和对缓存的要求最终将增加应用的复杂性,需要自定义 Electron 代码。

高复杂性应用程序

对于最高级别的复杂性,让我们看一下像 Sharp 这样的批处理图像处理应用程序。该应用程序必须能够处理数千个图像并完全离线工作。

离线应用程序与前两个示例明显不同。具体而言,典型的后端工作负载(例如图像处理)将通过创建离线应用程序在 Electron 中执行。

主要特点:

  • 我们的大多数代码将是在桌面应用程序中自定义的
  • 该应用将具有自己的发行节奏
  • 底层逻辑将从 Electron 内部运行(即,在渲染过程中)
  • 可以使用标准的Web开发工具,但取决于定义的架构
  • 我们可能需要使用本机模块,例如数据库访问,图像处理或机器学习
  • 从多个渲染进程中都可访问 Electron API,尤其是对于跨进程通信(IPC)

架构设计

对于架构建议,让我们考虑上述脱机图像处理应用程序。
浅谈 Electron.js 中的架构设计
遵循 Electron 官方文档构建了该应用的架构模式,带来了一些局限性。首先,在隐藏的渲染进程中执行长时间的CPU密集型操作时,会导致性能降低。

请注意,绝不能在主进程中运行CPU密集型操作。这样做可能会阻塞主进程,从而导致您的应用程序卡住或者崩溃。

此外,将业务逻辑和通信层与 Electron API耦合限制了重复使用 Web 开发工具的原则。主进程和渲染器进程之间的通信使用IPC,在两个渲染过程之间进行通信时,需要通过主进程进行信息的来回传递。

如果您的应用程序属于低或中复杂性类别,恭喜!离线应用程序中出现的许多坑都不会遇到。但是,如果您的应用程序需求落在高复杂性范围内,那就难说了!

高级架构建议

当我们考虑诸如性能降级,渲染过程之间的往返信息传递等离线应用程序中的问题以及整体开发人员的体验时,我们需要专门的体系结构:
浅谈 Electron.js 中的架构设计
这些架构建议建立在以下条件之上:

  • 前端和后端之间共享的代码被提取到一个模块中
  • UI代码是和 Electron 完全解耦的,因此可以直接复用 Web 开发的最佳实践
  • UI和页面路由是由受控组件和集中式应用程序状态管理的
  • 后端是在单独的node.js进程运行的
  • 前端和后端模块通过 IPC 通信

让我们详细介绍每个模块!

注意:部分技术栈纯粹是依据个人喜好而选择的,并且可以使用其它技术替换。例如,您可以将 typescript 交、替换为JavaScript,React 替换 VUE,REDUX 替换 MOBX或使用其它的 NPM软件包管理器替换 yarn,而不是完全遵照上述示例。只要尊重上述思路,您就可以自由选择技术栈。

共享模块

共享模块负责前端和后端模块共享的代码和类型。它使您能够开发两个模块作为独立实体,同时仍共享与域相关的代码和类型。

使用 yarn workspace 来实现 CodeShing,这是将模块作为NPM软件包发布,更新和版本化的简单替代方法。

主要特点:

  • Typescript 代码库
  • 消息传递通信的 Typescript:包含前端和后端所需的 payloads 和消息处理程序
  • 作用域模型和实体
  • 共享工具方法如日志和事件

前端模块

前端模块负责UI的所有内容。它包含我们应用程序的组件和动画,但不包含业务逻辑。在生产环境中,Electron 从生成的静态文件中使用它。

主要特点:

  • Typescript 代码库,访问共享模块
  • 使用 React 与Create React App 作为模板建立用户界面
  • 使用 Redux 作为状态管理,定义 UI 的实时渲染状态
  • 通过消息传递与后端通信:前端暴露了一个消息处理程序,该消息处理程序监听后端传递过来消息并修改相应的 Redux Store 中的状态
  • 使用 storybook 隔离组件开发

Electron 的后端模块

后端模块包含后端代码库和电子设置代码。业务逻辑和长期运行的操作(例如图像处理)将在单独的node.js流程中运行,以使UI不会遭受降级性能。

主要特点:

  • Typescript 代码库,访问共享模块
  • 后端作为fork node.js 进程运行,可改善长时间运行和执行昂贵计算任务的性能
  • 访问本地依赖
  • 执行与 Electron 版本相匹配的本机依赖关系的前置构建步骤
  • 包含所需的 Electron 配置和 packaging 脚本

通讯层

前端和后端使用 node-ipc 传递的跨进程消息进行通信。消息传递允许异步和基于事件的通信。

异步通信最适合短时任务。前端可以在后端成功处理消息后立即获取结果。

基于事件的通信更适合花费长时间的任务,例如批处理。随着后端的任务处理,它可以通知前端更新 Redux 中的应用程序状态。后端可以异步完成长期运行的任务,并周期性的更新前端UI显示。

主要特点:

  • 使用 node-IPC 作为通信库
  • 共享模块中的标准化数据载荷和事件处理程序
  • 异步和基于消息的通信支持

总结

Electron 是使用不同的Web技术构建跨平台桌面应用程序的不错选择。尽管 Electron 更适合在低复杂性应用中使用,但是随着复杂性的增加,性能和开发人员的经验和能力水平将显得更为重要。

本文所提出的架构旨在为高复杂性应用程序提供合理的理论基础。当然,它可能需要根据具体情况进行扩展,而且它为不同类型应用程序提供了很好的理论支持。

原文链接:https://juejin.cn/post/7256176612197498938 作者:Bravooo_

(0)
上一篇 2023年7月17日 上午10:06
下一篇 2023年7月17日 上午10:16

相关推荐

发表回复

登录后才能评论