广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Electron架构深入探究
  • 557
分享到

Electron架构深入探究

Electron架构Electron架构探究 2023-02-09 12:02:36 557人浏览 独家记忆
摘要

目录Electron是什么Electron架构小结Electron是什么 引用来自官网的解释: Electron 是一个使用 javascript、 html 和 CSS 构建桌面

Electron是什么

引用来自官网的解释:

Electron 是一个使用 javascripthtmlCSS 构建桌面应用程序的框架。通过将 Chromium 和 node.js 嵌入到它的二进制文件中,Electron 允许你维护一个 JavaScript 代码库,并创建可以在 windowsMacOS 和 linux 上运行的跨平台应用程序ーー不需要本地开发经验。

如果我们追溯历史,可以发现Electron的前身是Atom shell,最开始的设计目标就是为Atom编辑器而生,实际上Electron的作者之前也是node-WEBkit的核心贡献者,而node-webkit正是NW.js的前身。在作者离开英特尔公司(当初大力支持node-webkit)后,就加入了GitHub项目组,孵化了Atom Shell这个项目,再到后来(2014年)正式更名为Electron,发展至今。

Electron和众多类似的产品目标非常简单,他们将chromium(这里不同的框架选择不同,比如Tauri选择了原生的WebView,而WebView2选择了Edge内核)和Node.js(这里不同框架采取策略也不同,比如Webview2不提供默认的运行时,而Tauri选择了Rust等等)利用c++等原生语言集成起来,提供了一整套基于Web的运行环境,并提供了与底层OS交互的便捷api,目的就是为了让大家使用Web的技术栈去开发客户端原生应用,从而实现不同操作系统之间的跨平台开发。

说起桌面端GUI跨平台开发不得不提到Qt,这个使用C++来统一各个平台开发的框架才能被称为桌面跨端开发的鼻祖。在之前每个操作系统都是各自为战,经历过windows时代的MFCWPF的开发模式,在Mac上则是OCSwift作为底层语言,在经历了互联网时代变迁后,跨端框架越来越被大家所喜爱,广泛运用于互联网产品中。

若论起性能,肯定是越接近底层的语言开发出来的软件性能越高。如果要追溯历史,最底层的编程方式大概是最早的纸带打孔编码的时代,那时的人们是真正的编程大师,在用机器码编程,写二进制程序是非常复杂的,只通过0和1两个数字完成如此复杂的逻辑代码,基本上是需要巨大的精力和时间的,所以这个时代的程序只能做非常简单的事情。到后来汇编语言的产生才能让一些操作逐渐复杂一些,但依旧无法完成更复杂的架构设计。直到高级语言的出现,才让软件世界逐渐丰富多彩了起来,在计算机上产生了令人震惊的软件和操作系统,进入个人电脑时代,改变了世界。

在高级语言中,C语言应该是最接近底层的开发语言了,因此常用的操作系统内核基本都是用C语言和汇编写成,这是因为内核需要极高的性能以及对底层硬件驱动的精确控制。后来经过工程师在工程方面的不断探索,有了面向对象,编程范式,有更高层利于维护的语言出现。C++可以说是介于C和更高级语言之间的一个产物,它完全兼容C的语法,又添加了面向对象、STL模板等能力,让他在能拥有不俗的性能的同时,还拥有一定的大型工程化的能力,我们所了解到的大多数最复杂的软件,比如浏览器Chrome、Office、编译器、游戏等等,尤其是最复杂的浏览器软件,浏览器内核的复杂程度远超我们想象,也正是有chromium这样的开源贡献,造就了今天互联网时代的技术繁荣,基于V8LibUV构建的Node.js前端迅速发展到一个不可思议的地步,本文所讲的Electron也同样依赖于这样的技术。

回到性能的讨论,毫无疑问在引入了chromiumNode.js作为运行时的Electron性能是不如基于C++Qt,同样Qt的性能也不及直接使用原生语言来开发的程序,多一层抽象的封装必然会折损一部分性能。那是不是说用性能越好的语言来开发就是最优的呢?答案显然不是,不然现在我们应该直接用机器码来开发。实际上,软件语言和框架的发展,离不开硬件的高速发展,硬件遵循着摩尔定律发展至今,硬件的计算能力、存储能力都有着飞跃式的进步,这才能够让软件脱离底层的束缚,越来越专注于工程的可维护性和易用性,释放掉一些不必要的生产力,让软件的世界发展到今天这种繁荣的地步。

毫无疑问,Electron是处于最上层的抽象层级当中,通过简单高效的Web技术来实现复杂的桌面软件开发。从语言发展的视角来看,C和C++可以对内存进行精细地控制访问,而底层的汇编则更可以操纵寄存器,存储越靠近CPU运行速度就会越快。但是它带来的代价是昂贵的开发成本和安全问题,还记得在windows时代有着各种蠕虫木马病毒,很大一部分都是程序本身的缓冲区溢出、访问越界等等问题,这类底层的语言对开发人员的要求较高,需要能够掌握操作系统的内部机制,在大型工程的情况下,bug是难免的,而底层语言造成的bug往往影响面更大。因此后来Java等语言去除了指针的设计,弱化了对内存的控制程度,增强了工程化的能力,以更好地应对复杂的软件环境。再到后来pythonjavascript等脚本语言的流行,更简化了开发人员编写代码的成本,涌现多种编程范式和繁荣的生态。而新兴的现代化编程语言golangrust等则是性能与易用之间的权衡,从这个发展趋势来看,大家一直在探寻着一个平衡点。

从另一个角度来看,软件的研发成本和产品、商业也息息相关,行业巨头Google、微软、IBM等大型软件公司,都曾面临过无法按时交付的问题。人月神话中也深刻地揭示了软件开发的工期不是靠纯堆人力就能将问题解决的,一旦有这类风险产生,无论是对企业还是用户都会造成比较大的伤害。而越是底层的语言,所需要软件开发人员的专业素质越高,维护成本也就越高,因此在互联网高速发展的时代,系统开发相关的人才不断减少,更多涌现出大量的应用开发人员,进入Web高速发展的阶段。这是由于互联网业务的高速发展、软硬件技术的不断迭代更新,形成的一种趋势。因此ElectronReact NativeFlutteruniAppTaro这类跨端解决方案越来越受大家欢迎也是必然的结果。

Electron架构

ElectronJS 进程模型图解

Electron的架构设计很大程度上是受到Chromium的启发。ChromiumGoogle开源的浏览器内核代码,ChromeEdgeOpera等等浏览器都是基于Chromium来实现的。Chromium堪称是全世界最复杂的软件应用,整体代码库已经庞大到了近40G,我们简单举个例子说明下其复杂程度:

  • net模块,实现了主机解析,cookies,网络改变探测,SSL,资源缓存,ftp,Http, OCSP实现,代理 (SOCKS和HTTP) 配置,解析,脚本获取(包括各种不同系统下实现),QUIC,Socket池,SPDY,websockets等等,每套协议都是十分复杂的。
  • v8模块,包括字节码解析器,JIT 编译器,多代GC,inspector (调试支持),内存和 CPU 的 profiler(性能统计),WebAssembly 支持,两种 post-mortem diagnostics 的支持,启动快照,代码缓存、代码热点分析等等。
  • Skia模块,用点画出各种图。然而里面包括十几种矢量的绘制,文字绘制、GPU加速、矢量的指令录制以及回放(还要能支持线程安全)、各种图像格式的编解码、GPU渲染优化等等。
  • Blink内核,这个更复杂,HTMLCSS规范就得近万页了,要将它们完全实现是一个巨大的工作量,再加上实现Layout的成本,涉及到非常庞大的计算,还要考虑极致的性能,保证浏览器的渲染能够流畅快速。
  • 还有音视频相关、沙箱、插件、UI等等,就不赘述了,总之称之为巨型软件应用也不为过,

正是由于这一庞大复杂架构的开源,经过数年的不断打磨完善,让Web技术能够快速发展,百花齐放。

我们知道,在浏览器内部有很多的处理模块,这些模块的代码可以采用进程或者线程的方式来进行组织调度:

browser architecture

在浏览器实现方面是没有标准规范的,这取决于不同浏览器内部的实现细节。在最早期,浏览器渲染都是在同一个进程里面完成的,后来Chrome采用了多进程架构,演变成了这种模式:

browser architecture

其中:

  • 浏览器主进程,实现浏览器的主要UI、负责和文件、网络等等操作系统底层接口对接通信。
  • 渲染进程,每一个tab独立开一个渲染进程,核心进行web代码解析和渲染工作。
  • 插件进程,负责浏览器插件的控制。
  • GPU进程,独立的进程负责处理GPU图像的渲染绘制。

这样设计架构的好处主要有两个:

  • 保证每个tab独立进程,这样在某一个页面crash的时候,仅仅影响当前的Tab,而不至于让整个浏览器崩溃。这提升了软件的健壮性和用户体验。
  • 有利于实现沙箱隔离的安全机制,基于进程可以很方便地控制不同页面之间的安全访问策略,确保每个renderer进程在自己单独的沙箱环境内安全地运行。

不过这类架构也带来一个额外的成本,那就是更多的内存消耗,因为每一个进程需要开辟独立的内存空间,不同进程之间的内存很难做到共享。因此这个策略也是个权衡的策略考虑,Chrome基于用户当前的CPU和内存的情况,对进程的数量进行了动态的限制,以确保达到一个最佳的效果。

Electron本身参考了这个架构的实现,将各个GUI窗口通过renderer进程实现,交由chromium来加载渲染,主进程集成Node.js,负责与系统API交互,处理核心事务

我们可以从Electron源码结构上很清晰地看到这点:

Electron
├── build/ - 构建相关
├── buildflags/ - feature flag
├── chromium_src/ - chromium的一份拷贝(源码仅包含build文件)
├── default_app/ - 默认启动时的app程序
├── docs/ -文档
├── lib/ - 使用JS/TS编写的模块
|   ├── browser/ - 主进程初始化相关
|   |   ├── api/ - 主进程暴露的API,通过_linkedBinding调用C++模块
|   ├── common/ - 主进程和渲染进程共用代码
|   |   └── api/ - 主进程和渲染进程共同暴露的API
|   ├── renderer/ - 渲染进程初始化相关
|   |   ├── api/ - 渲染进程API
|   |   └── web-view/ - webview相关逻辑
├── patches/ - 关于依赖的一些patch,主要是chromium、node、v8的
├── shell/ - C++编写的模块
|   ├── app/ - 入口
|   ├── browser/ - 主进程相关
|   |   ├── ui/ - 系统UI组件的一些实现
|   |   ├── api/ - 主进程API实现
|   |   ├── net/ - 网络相关实现
|   |   ├── mac/ - Mac系统下的一些实现(OC实现)
|   ├── renderer/ - 渲染进程相关
|   |   └── api/ - 渲染进程API实现
|   └── common/ - 主进程渲染进程通用实现
|       └── api/ - 主进程渲染进程通用API实现
└── BUILD.gn - 构建入口

Electron在源码上与Node.js实现类似,lib目录使用js实现,通过_linkedBinding来调用C++的模块,在一开始会被编译进内存中,通过C++程序装在启动。

C++启动入口在shell/app/electron_library_main.mm:

int ElectronMain(int argc, char* argv[]) {
  electron::ElectronMainDelegate delegate;
  content::ContentMainParams params(&delegate);
  params.argc = argc;
  params.argv = const_cast<const char**>(argv);
  electron::ElectronCommandLine::Init(argc, argv);
  return content::ContentMain(std::move(params));
}

这里依赖的content实际上是chromiumcontent模块,chromium将浏览器主进程部分和渲染进程进行了抽象,主进程部分的逻辑在blink模块实现,而渲染进程相关的实现则封装在content模块。

所以这里调用content就可以使用chromiumrenderer进程来进行启动渲染了。

关于C++部分的核心实现会在后续文章中进一步探讨。

小结

Electron本质上就是集成Node.jsChromium提供了基于Web技术栈开发客户端软件的能力,通过Web技术栈的快速迭代和Chrome本身的向前兼容迭代能力,可以做到跨平台的快速迭代开发。

在软件发展的进程中,我们始终在平衡软硬件之间的性能关系,性能与成本的平衡,软件维护和生态的持续发展,基于互联网的发展,Web技术的发展,促成了Electron这样的跨端技术被广泛应用。

Electron在实现架构上参考了Chromium的核心设计思想,通过主进程进行核心的调度启动,不同的GUI窗口独立渲染进程,并提供沙箱安全机制,做到进程间的隔离,进程与进程之间实现了IPC通信机制,对主进程提供Node.js运行时,封装上层API,通过C++提供具体系统组件、系统方法能力的实现。

以上就是Electron架构深入探究的详细内容,更多关于Electron架构的资料请关注编程网其它相关文章!

--结束END--

本文标题: Electron架构深入探究

本文链接: https://www.lsjlt.com/news/195305.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Electron架构深入探究
    目录Electron是什么Electron架构小结Electron是什么 引用来自官网的解释: Electron 是一个使用 JavaScript、 HTML 和 CSS 构建桌面...
    99+
    2023-02-09
    Electron架构 Electron架构探究
  • 深入探究node之Transform
    本文详细的介绍了node Transform ,分享给大家,希望此文章对各位有所帮助。 Transform流特性 在开发中直接接触Transform流的情况不是很多,往往是使用相对成熟的模块或者封装的API...
    99+
    2022-06-04
    node Transform
  • AndroidHandler源码深入探究
    1.android 消息循环有4个重要的类Handler、Message、Looper、MessageQueue handler 用来发送、处理消息。 Message 是消息的载体。...
    99+
    2022-11-13
  • ABP基础架构深入探索
    目录前言一、了解模块化Startup 类模块定义模块依赖和启动模块模块生命周期二、使用依赖注入系统.NET 原生依赖注入ABP的依赖注入1.约定式注册2.接口注册3.属性注册4.接口...
    99+
    2022-11-13
  • C++数据结构深入探究栈与队列
    目录1. 栈1.1 栈的概念1.2 栈的实现2. 队列2.1 队列的概念2.2 队列的实现3. 栈和队列面试题3.1 括号匹配问题3.2用队列实现栈3.3 用栈实现队列3.4 设计循...
    99+
    2022-11-13
  • Golang中map的深入探究
    目录简介Map 的底层内存模型Map 的存与取底层代码Map 的扩容第一种情况第二种情况Map 的有序性Map 的并发总结简介 本文主要通过探究在golang 中map的数据结构及源...
    99+
    2022-11-11
  • VueComputed底层原理深入探究
    今天面了家小公司,上来直接问 computed 底层原理,面试官是这样问的,data 中定义了 a 和 b 变量。computed 里面定义了 c 属性,c 的结果依赖与 a 和 b...
    99+
    2022-11-13
  • iOS坐标系的深入探究
    前言 app在渲染视图时,需要在坐标系中指定绘制区域。 这个概念看似乎简单,事实并非如此。 When an app draws something in iOS, it has...
    99+
    2022-05-15
    ios 坐标系 转换
  • Android 中的注解深入探究
    本文系GDG Android Meetup分享内容总结文章 注解是我们经常接触的技术,Java有注解,Android也有注解,本文将试图介绍Android中的注解,以及Butt...
    99+
    2022-06-06
    注解 Android
  • C++深入探究友元使用
    目录友元特点外部函数友元成员函数友元总结类友元友元 友元 friend 机制允许一个类授权其他的函数访问它的非公有成员. 友元声明以关键字 friend 开头 ,它只能出现在类的声明...
    99+
    2022-11-13
  • Java synchronized与死锁深入探究
    目录1.synchronized的特性2.synchronized使用示例:3.Java标准库中的线程安全类4.死锁是什么5.如果避免死锁1.synchronized的特性 1). ...
    99+
    2023-01-30
    Java synchronized Java 死锁
  • 【SpringCloud】深入探究Eureka:构建微服务架构中的高效服务发现系统
    👨‍💻博主主页:小尘要自信 在现代的软件开发中,微服务架构已经成为了一个热门的话题。微服务架构的一个关键组成部分就是服务发现。而在服务发现领域,Eureka无疑是...
    99+
    2023-09-07
    spring cloud eureka 微服务 java 后端
  • 深入探究Java线程的创建与构造方法
    目录一、创建线程启动线程—start 方法方法一方法二方法三方法四方法五方法六二、run方法和start方法的区别①方法性质不同②执行速度不同③调用次数不同总结三、线程的...
    99+
    2022-11-13
  • Java深入探究Object类的方法
    目录1.equals方法1.API中equals方法的介绍2.==和equals 的对比2.hashCode方法3.toString方法4.finalize方法本文主要带大家看看Ob...
    99+
    2022-11-13
  • C/C++多态深入探究原理
    目录多态虚表和虚表指针多态 面向对象编程有三大特性:继承、封装和多态。 其中,多态又分为编译时多态和运行时多态。编译多态是通过重载函数体现的,运行多态是通过虚函数体现的。 多态是如何...
    99+
    2022-11-13
  • C/C++函数指针深入探究
    目录函数指针语法函数地址声明使用函数指针调用函数深入理解函数指针使用typedef 简化函数指针 为什么要使用函数指针? 调用的灵活性和通用性。 试想一下,我们在设计初期并不知道我们...
    99+
    2022-11-13
  • C++深入探究引用的使用
    目录一. 引用的概念二. 引用特性三. 常引用四. 使用场景1. 做参数2. 做返回值3. 做返回值需要注意的问题五. 传值传引用效率对比1. 值和引用传参时的效率比较2. 值和引用...
    99+
    2022-11-13
  • MySQL中join查询的深入探究
    目录前引索引对 join 查询的影响数据准备有索引查询过程无索引查询过程了解 Block Nested-Loop JoinBlock Nested-Loop Join查询过程Join...
    99+
    2022-11-13
    mysql join查询 流程 mysql join方式 mysql join查询
  • Vue中v-bind原理深入探究
    目录前置内容解析模板总结前面我们分析了v-model的原理,接下来我们看看v-bind的实现又是怎样的呢? 前置内容 <template> <div> ...
    99+
    2022-11-13
    Vue v-bind Vue v-bind原理 Vue v-bind作用
  • SQL深入探究存储的过程
    目录存储过程简介存储过程的创建及调用存储过程的删除存储过程的优缺点现需要向学生表中插入新的学生数据。但在插入学生数据的时,需要同 时检查老师表里的数据。如果插入学生的老师不在老师表里,则先向老师表中插入一条老师数据,再向...
    99+
    2023-01-05
    SQL存储过程的作用 SQL存储过程
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作