Electron架構(gòu)深入探究
Electron是什么
引用來自官網(wǎng)的解釋:
Electron 是一個(gè)使用 JavaScript、 HTML 和 CSS 構(gòu)建桌面應(yīng)用程序的框架。通過將 Chromium 和 Node.js 嵌入到它的二進(jìn)制文件中,Electron 允許你維護(hù)一個(gè) JavaScript 代碼庫(kù),并創(chuàng)建可以在 Windows、 macOS 和 Linux 上運(yùn)行的跨平臺(tái)應(yīng)用程序ーー不需要本地開發(fā)經(jīng)驗(yàn)。
如果我們追溯歷史,可以發(fā)現(xiàn)Electron
的前身是Atom Shell
,最開始的設(shè)計(jì)目標(biāo)就是為Atom
編輯器而生,實(shí)際上Electron
的作者之前也是node-webkit
的核心貢獻(xiàn)者,而node-webkit
正是NW.js
的前身。在作者離開英特爾公司(當(dāng)初大力支持node-webkit
)后,就加入了Github
項(xiàng)目組,孵化了Atom Shell
這個(gè)項(xiàng)目,再到后來(2014年)正式更名為Electron
,發(fā)展至今。
Electron
和眾多類似的產(chǎn)品目標(biāo)非常簡(jiǎn)單,他們將chromium
(這里不同的框架選擇不同,比如Tauri
選擇了原生的WebView
,而WebView2
選擇了Edge
內(nèi)核)和Node.js
(這里不同框架采取策略也不同,比如Webview2
不提供默認(rèn)的運(yùn)行時(shí),而Tauri
選擇了Rust
等等)利用C++
等原生語言集成起來,提供了一整套基于Web
的運(yùn)行環(huán)境,并提供了與底層OS
交互的便捷API
,目的就是為了讓大家使用Web
的技術(shù)棧去開發(fā)客戶端原生應(yīng)用,從而實(shí)現(xiàn)不同操作系統(tǒng)之間的跨平臺(tái)開發(fā)。
說起桌面端GUI跨平臺(tái)開發(fā)不得不提到Qt
,這個(gè)使用C++
來統(tǒng)一各個(gè)平臺(tái)開發(fā)的框架才能被稱為桌面跨端開發(fā)的鼻祖。在之前每個(gè)操作系統(tǒng)都是各自為戰(zhàn),經(jīng)歷過windows
時(shí)代的MFC
、WPF
的開發(fā)模式,在Mac
上則是OC
、Swift
作為底層語言,在經(jīng)歷了互聯(lián)網(wǎng)時(shí)代變遷后,跨端框架越來越被大家所喜愛,廣泛運(yùn)用于互聯(lián)網(wǎng)產(chǎn)品中。
若論起性能,肯定是越接近底層的語言開發(fā)出來的軟件性能越高。如果要追溯歷史,最底層的編程方式大概是最早的紙帶打孔編碼的時(shí)代,那時(shí)的人們是真正的編程大師,在用機(jī)器碼編程,寫二進(jìn)制程序是非常復(fù)雜的,只通過0和1兩個(gè)數(shù)字完成如此復(fù)雜的邏輯代碼,基本上是需要巨大的精力和時(shí)間的,所以這個(gè)時(shí)代的程序只能做非常簡(jiǎn)單的事情。到后來匯編語言的產(chǎn)生才能讓一些操作逐漸復(fù)雜一些,但依舊無法完成更復(fù)雜的架構(gòu)設(shè)計(jì)。直到高級(jí)語言的出現(xiàn),才讓軟件世界逐漸豐富多彩了起來,在計(jì)算機(jī)上產(chǎn)生了令人震驚的軟件和操作系統(tǒng),進(jìn)入個(gè)人電腦時(shí)代,改變了世界。
在高級(jí)語言中,C語言應(yīng)該是最接近底層的開發(fā)語言了,因此常用的操作系統(tǒng)內(nèi)核基本都是用C語言和匯編寫成,這是因?yàn)閮?nèi)核需要極高的性能以及對(duì)底層硬件驅(qū)動(dòng)的精確控制。后來經(jīng)過工程師在工程方面的不斷探索,有了面向?qū)ο?,編程范式,有更高層利于維護(hù)的語言出現(xiàn)。C++可以說是介于C和更高級(jí)語言之間的一個(gè)產(chǎn)物,它完全兼容C的語法,又添加了面向?qū)ο蟆TL模板等能力,讓他在能擁有不俗的性能的同時(shí),還擁有一定的大型工程化的能力,我們所了解到的大多數(shù)最復(fù)雜的軟件,比如瀏覽器Chrome、Office、編譯器、游戲等等,尤其是最復(fù)雜的瀏覽器軟件,瀏覽器內(nèi)核的復(fù)雜程度遠(yuǎn)超我們想象,也正是有chromium
這樣的開源貢獻(xiàn),造就了今天互聯(lián)網(wǎng)時(shí)代的技術(shù)繁榮,基于V8
和LibUV
構(gòu)建的Node.js
讓前端迅速發(fā)展到一個(gè)不可思議的地步,本文所講的Electron
也同樣依賴于這樣的技術(shù)。
回到性能的討論,毫無疑問在引入了chromium
和Node.js
作為運(yùn)行時(shí)的Electron
性能是不如基于C++
的Qt
,同樣Qt
的性能也不及直接使用原生語言來開發(fā)的程序,多一層抽象的封裝必然會(huì)折損一部分性能。那是不是說用性能越好的語言來開發(fā)就是最優(yōu)的呢?答案顯然不是,不然現(xiàn)在我們應(yīng)該直接用機(jī)器碼來開發(fā)。實(shí)際上,軟件語言和框架的發(fā)展,離不開硬件的高速發(fā)展,硬件遵循著摩爾定律發(fā)展至今,硬件的計(jì)算能力、存儲(chǔ)能力都有著飛躍式的進(jìn)步,這才能夠讓軟件脫離底層的束縛,越來越專注于工程的可維護(hù)性和易用性,釋放掉一些不必要的生產(chǎn)力,讓軟件的世界發(fā)展到今天這種繁榮的地步。
毫無疑問,Electron
是處于最上層的抽象層級(jí)當(dāng)中,通過簡(jiǎn)單高效的Web技術(shù)來實(shí)現(xiàn)復(fù)雜的桌面軟件開發(fā)。從語言發(fā)展的視角來看,C和C++可以對(duì)內(nèi)存進(jìn)行精細(xì)地控制訪問,而底層的匯編則更可以操縱寄存器,存儲(chǔ)越靠近CPU運(yùn)行速度就會(huì)越快。但是它帶來的代價(jià)是昂貴的開發(fā)成本和安全問題,還記得在windows時(shí)代有著各種蠕蟲木馬病毒,很大一部分都是程序本身的緩沖區(qū)溢出、訪問越界等等問題,這類底層的語言對(duì)開發(fā)人員的要求較高,需要能夠掌握操作系統(tǒng)的內(nèi)部機(jī)制,在大型工程的情況下,bug是難免的,而底層語言造成的bug往往影響面更大。因此后來Java
等語言去除了指針的設(shè)計(jì),弱化了對(duì)內(nèi)存的控制程度,增強(qiáng)了工程化的能力,以更好地應(yīng)對(duì)復(fù)雜的軟件環(huán)境。再到后來python
、javascript
等腳本語言的流行,更簡(jiǎn)化了開發(fā)人員編寫代碼的成本,涌現(xiàn)多種編程范式和繁榮的生態(tài)。而新興的現(xiàn)代化編程語言如golang
、rust
等則是性能與易用之間的權(quán)衡,從這個(gè)發(fā)展趨勢(shì)來看,大家一直在探尋著一個(gè)平衡點(diǎn)。
從另一個(gè)角度來看,軟件的研發(fā)成本和產(chǎn)品、商業(yè)也息息相關(guān),行業(yè)巨頭Google、微軟、IBM等大型軟件公司,都曾面臨過無法按時(shí)交付的問題。人月神話中也深刻地揭示了軟件開發(fā)的工期不是靠純堆人力就能將問題解決的,一旦有這類風(fēng)險(xiǎn)產(chǎn)生,無論是對(duì)企業(yè)還是用戶都會(huì)造成比較大的傷害。而越是底層的語言,所需要軟件開發(fā)人員的專業(yè)素質(zhì)越高,維護(hù)成本也就越高,因此在互聯(lián)網(wǎng)高速發(fā)展的時(shí)代,系統(tǒng)開發(fā)相關(guān)的人才不斷減少,更多涌現(xiàn)出大量的應(yīng)用開發(fā)人員,進(jìn)入Web高速發(fā)展的階段。這是由于互聯(lián)網(wǎng)業(yè)務(wù)的高速發(fā)展、軟硬件技術(shù)的不斷迭代更新,形成的一種趨勢(shì)。因此Electron
、React Native
、Flutter
、uniApp
、Taro
這類跨端解決方案越來越受大家歡迎也是必然的結(jié)果。
Electron架構(gòu)
ElectronJS 進(jìn)程模型圖解
Electron
的架構(gòu)設(shè)計(jì)很大程度上是受到Chromium
的啟發(fā)。Chromium
是Google
開源的瀏覽器內(nèi)核代碼,Chrome
、Edge
、Opera
等等瀏覽器都是基于Chromium
來實(shí)現(xiàn)的。Chromium
堪稱是全世界最復(fù)雜的軟件應(yīng)用,整體代碼庫(kù)已經(jīng)龐大到了近40G,我們簡(jiǎn)單舉個(gè)例子說明下其復(fù)雜程度:
net
模塊,實(shí)現(xiàn)了主機(jī)解析,cookies,網(wǎng)絡(luò)改變探測(cè),SSL,資源緩存,ftp,HTTP, OCSP實(shí)現(xiàn),代理 (SOCKS和HTTP) 配置,解析,腳本獲?。òǜ鞣N不同系統(tǒng)下實(shí)現(xiàn)),QUIC,socket池,SPDY,WebSockets等等,每套協(xié)議都是十分復(fù)雜的。v8
模塊,包括字節(jié)碼解析器,JIT 編譯器,多代GC,inspector (調(diào)試支持),內(nèi)存和 CPU 的 profiler(性能統(tǒng)計(jì)),WebAssembly 支持,兩種 post-mortem diagnostics 的支持,啟動(dòng)快照,代碼緩存、代碼熱點(diǎn)分析等等。Skia
模塊,用點(diǎn)畫出各種圖。然而里面包括十幾種矢量的繪制,文字繪制、GPU加速、矢量的指令錄制以及回放(還要能支持線程安全)、各種圖像格式的編解碼、GPU渲染優(yōu)化等等。Blink
內(nèi)核,這個(gè)更復(fù)雜,HTML
和CSS
規(guī)范就得近萬頁(yè)了,要將它們完全實(shí)現(xiàn)是一個(gè)巨大的工作量,再加上實(shí)現(xiàn)Layout
的成本,涉及到非常龐大的計(jì)算,還要考慮極致的性能,保證瀏覽器的渲染能夠流暢快速。- 還有音視頻相關(guān)、沙箱、插件、UI等等,就不贅述了,總之稱之為巨型軟件應(yīng)用也不為過,
正是由于這一龐大復(fù)雜架構(gòu)的開源,經(jīng)過數(shù)年的不斷打磨完善,讓W(xué)eb技術(shù)能夠快速發(fā)展,百花齊放。
我們知道,在瀏覽器內(nèi)部有很多的處理模塊,這些模塊的代碼可以采用進(jìn)程或者線程的方式來進(jìn)行組織調(diào)度:
browser architecture
在瀏覽器實(shí)現(xiàn)方面是沒有標(biāo)準(zhǔn)規(guī)范的,這取決于不同瀏覽器內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。在最早期,瀏覽器渲染都是在同一個(gè)進(jìn)程里面完成的,后來Chrome
采用了多進(jìn)程架構(gòu),演變成了這種模式:
browser architecture
其中:
- 瀏覽器主進(jìn)程,實(shí)現(xiàn)瀏覽器的主要UI、負(fù)責(zé)和文件、網(wǎng)絡(luò)等等操作系統(tǒng)底層接口對(duì)接通信。
- 渲染進(jìn)程,每一個(gè)
tab
獨(dú)立開一個(gè)渲染進(jìn)程,核心進(jìn)行web代碼解析和渲染工作。 - 插件進(jìn)程,負(fù)責(zé)瀏覽器插件的控制。
- GPU進(jìn)程,獨(dú)立的進(jìn)程負(fù)責(zé)處理GPU圖像的渲染繪制。
這樣設(shè)計(jì)架構(gòu)的好處主要有兩個(gè):
- 保證每個(gè)tab獨(dú)立進(jìn)程,這樣在某一個(gè)頁(yè)面
crash
的時(shí)候,僅僅影響當(dāng)前的Tab,而不至于讓整個(gè)瀏覽器崩潰。這提升了軟件的健壯性和用戶體驗(yàn)。 - 有利于實(shí)現(xiàn)沙箱隔離的安全機(jī)制,基于進(jìn)程可以很方便地控制不同頁(yè)面之間的安全訪問策略,確保每個(gè)
renderer
進(jìn)程在自己?jiǎn)为?dú)的沙箱環(huán)境內(nèi)安全地運(yùn)行。
不過這類架構(gòu)也帶來一個(gè)額外的成本,那就是更多的內(nèi)存消耗,因?yàn)槊恳粋€(gè)進(jìn)程需要開辟獨(dú)立的內(nèi)存空間,不同進(jìn)程之間的內(nèi)存很難做到共享。因此這個(gè)策略也是個(gè)權(quán)衡的策略考慮,Chrome
基于用戶當(dāng)前的CPU
和內(nèi)存的情況,對(duì)進(jìn)程的數(shù)量進(jìn)行了動(dòng)態(tài)的限制,以確保達(dá)到一個(gè)最佳的效果。
Electron
本身參考了這個(gè)架構(gòu)的實(shí)現(xiàn),將各個(gè)GUI窗口通過renderer
進(jìn)程實(shí)現(xiàn),交由chromium
來加載渲染,主進(jìn)程集成Node.js
,負(fù)責(zé)與系統(tǒng)API交互,處理核心事務(wù)。
我們可以從Electron
源碼結(jié)構(gòu)上很清晰地看到這點(diǎn):
Electron ├── build/ - 構(gòu)建相關(guān) ├── buildflags/ - feature flag ├── chromium_src/ - chromium的一份拷貝(源碼僅包含build文件) ├── default_app/ - 默認(rèn)啟動(dòng)時(shí)的app程序 ├── docs/ -文檔 ├── lib/ - 使用JS/TS編寫的模塊 | ├── browser/ - 主進(jìn)程初始化相關(guān) | | ├── api/ - 主進(jìn)程暴露的API,通過_linkedBinding調(diào)用C++模塊 | ├── common/ - 主進(jìn)程和渲染進(jìn)程共用代碼 | | └── api/ - 主進(jìn)程和渲染進(jìn)程共同暴露的API | ├── renderer/ - 渲染進(jìn)程初始化相關(guān) | | ├── api/ - 渲染進(jìn)程API | | └── web-view/ - webview相關(guān)邏輯 ├── patches/ - 關(guān)于依賴的一些patch,主要是chromium、node、v8的 ├── shell/ - C++編寫的模塊 | ├── app/ - 入口 | ├── browser/ - 主進(jìn)程相關(guān) | | ├── ui/ - 系統(tǒng)UI組件的一些實(shí)現(xiàn) | | ├── api/ - 主進(jìn)程API實(shí)現(xiàn) | | ├── net/ - 網(wǎng)絡(luò)相關(guān)實(shí)現(xiàn) | | ├── mac/ - Mac系統(tǒng)下的一些實(shí)現(xiàn)(OC實(shí)現(xiàn)) | ├── renderer/ - 渲染進(jìn)程相關(guān) | | └── api/ - 渲染進(jìn)程API實(shí)現(xiàn) | └── common/ - 主進(jìn)程渲染進(jìn)程通用實(shí)現(xiàn) | └── api/ - 主進(jìn)程渲染進(jìn)程通用API實(shí)現(xiàn) └── BUILD.gn - 構(gòu)建入口
Electron
在源碼上與Node.js
實(shí)現(xiàn)類似,lib
目錄使用js
實(shí)現(xiàn),通過_linkedBinding
來調(diào)用C++
的模塊,在一開始會(huì)被編譯進(jìn)內(nèi)存中,通過C++
程序裝在啟動(dòng)。
C++
啟動(dòng)入口在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
實(shí)際上是chromium
的content
模塊,chromium
將瀏覽器主進(jìn)程部分和渲染進(jìn)程進(jìn)行了抽象,主進(jìn)程部分的邏輯在blink
模塊實(shí)現(xiàn),而渲染進(jìn)程相關(guān)的實(shí)現(xiàn)則封裝在content
模塊。
所以這里調(diào)用content
就可以使用chromium
的renderer
進(jìn)程來進(jìn)行啟動(dòng)渲染了。
關(guān)于C++
部分的核心實(shí)現(xiàn)會(huì)在后續(xù)文章中進(jìn)一步探討。
小結(jié)
Electron
本質(zhì)上就是集成Node.js
和Chromium
提供了基于Web技術(shù)棧開發(fā)客戶端軟件的能力,通過Web技術(shù)棧的快速迭代和Chrome本身的向前兼容迭代能力,可以做到跨平臺(tái)的快速迭代開發(fā)。
在軟件發(fā)展的進(jìn)程中,我們始終在平衡軟硬件之間的性能關(guān)系,性能與成本的平衡,軟件維護(hù)和生態(tài)的持續(xù)發(fā)展,基于互聯(lián)網(wǎng)的發(fā)展,Web技術(shù)的發(fā)展,促成了Electron
這樣的跨端技術(shù)被廣泛應(yīng)用。
Electron
在實(shí)現(xiàn)架構(gòu)上參考了Chromium
的核心設(shè)計(jì)思想,通過主進(jìn)程進(jìn)行核心的調(diào)度啟動(dòng),不同的GUI窗口獨(dú)立渲染進(jìn)程,并提供沙箱安全機(jī)制,做到進(jìn)程間的隔離,進(jìn)程與進(jìn)程之間實(shí)現(xiàn)了IPC
通信機(jī)制,對(duì)主進(jìn)程提供Node.js
運(yùn)行時(shí),封裝上層API,通過C++提供具體系統(tǒng)組件、系統(tǒng)方法能力的實(shí)現(xiàn)。
以上就是Electron架構(gòu)深入探究的詳細(xì)內(nèi)容,更多關(guān)于Electron架構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VSCode通過Ctrl+P快速打開node_modules中的文件的操作方法
由于node_modules目錄包含的文件太多,在VSCode中默認(rèn)情況下是禁止搜索node_modules目錄的,在這種情況下,我們將不得不依次展開node_modules的文件目錄樹,來查找我們所需要的文件,接下來介紹VSCode如何通過Ctrl+P快速打開node_modules中的文件,需要的朋友可以參考下2023-07-07詳解nodejs express下使用redis管理session
本篇文章主要介紹了詳解nodejs express下使用redis管理session ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04如何用node.js作為后臺(tái),讀寫xml文件,Node.js的文件系統(tǒng)的Api
這篇文章主要介紹了如何用node.js作為后臺(tái),讀寫xml文件,Node.js的文件系統(tǒng)的Api,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08在Node.js中實(shí)現(xiàn)獲取用戶頻道信息的功能
在構(gòu)建社交或視頻分享平臺(tái)時(shí),允許用戶查看其他用戶的頻道信息是一個(gè)基本需求,本文將介紹如何在Node.js應(yīng)用中,使用Express框架和Mongoose庫(kù)來實(shí)現(xiàn)這一功能,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2024-04-04node.js利用redis數(shù)據(jù)庫(kù)緩存數(shù)據(jù)的方法
Redis數(shù)據(jù)庫(kù)采用極簡(jiǎn)的設(shè)計(jì)思想,最新版的源碼包還不到2Mb。其在使用上也有別于一般的數(shù)據(jù)庫(kù)。下面這篇文章就來給大家介紹了node.js利用redis數(shù)據(jù)庫(kù)緩存數(shù)據(jù)的方法,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03Node.js中Sequelize?hook的使用方法小結(jié)
Sequelize?提供了多個(gè)?hook,用于在執(zhí)行數(shù)據(jù)庫(kù)操作時(shí)執(zhí)行一些自定義邏輯,本文為大家整理了一些常用的?Sequelize?hook?列表及其作用,希望對(duì)大家有所幫助2024-02-02Node.js多文件Stream合并,串行和并發(fā)兩種模式的實(shí)現(xiàn)方式
這篇文章主要介紹了Node.js多文件Stream合并,串行和并發(fā)兩種模式的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10