
互聯網出現之前,C/S 架構是軟體產品的主流,后面漸漸地被 B/S 架構所取代(因為不需要配置客戶端),但由于瀏覽器有重繪機制,服務器的負載等因素,C/S 架構的回應速度和流暢性是好于 B/S 架構的,所以現在軟體開發的趨勢是兩者的融合,一般是 B/S 架構開發的產品可以非常方便地轉移到 C/S 架構下,客戶端(client)是 C/S 架構軟體產品中重要的一部分,除了和用戶互動、本地處理資料的強大功能,順暢的體驗和美觀的樣式也是客戶端技術追求的目標,這里和大家介紹桌面版應用程式的一些歷史和現在比較流行的 electron 技術,
桌面版應用程式歷史
桌面應用程式,又稱為 GUI 程式,可以分為以下幾個階段:
- VB,上古程式員的開發工具,曾經全球第一的開發語言,拖拽式的圖形化開發讓它成為極佳的桌面開發工具,微軟依靠其作業系統的優勢,一直壓制同時期的競爭對手 delphi,微策略早期應用該技術,開發了管理智能商務平臺的大殺器 developer,
- C++、win32API 的 MFC 方案是基于視窗中組合控制元件和訊息傳遞機制,這也是 20 多年前的技術,所以 API 設計的不是很友好,幾年前微軟已經停止維護,簡單來說它已經過時了,
- Winform 微策略幾年前基于該技術研發第一代的 Desktop 版本,但是從開發體驗角度來說自定義、美化控制元件會比較麻煩,
- C# .net framework,代表就是 WPF,它的原生特性是其他類別庫無法比擬的:High DPI、Split Screen 以及對 DirectX 的天然優勢,但是并不開源,需要依賴.net 框架,還有就是啟動會比較慢,Workstation Windows 的新客戶端就是基于該技術研發,
- Java swing/javaFx,這是一類比較大的陣營,優勢是跨平臺和流行開發語言 Java 的天然結合,但開發出來的界面作者個人認為并不美觀,
- C++ Qt,這是很多客戶端跨平臺的首選,因為開源、UI 庫和各種功能的類別庫非常豐富,但是學習成本比較高,
- C++ duilib,這是 windows 下開源的 directUI(微軟提出的分離 UI 和邏輯的思想)庫,它是迎合互聯網桌面軟體小而美的趨勢發展起來的,可能大家對它的關注度比較少,但是用它開發出的產品大名鼎鼎,比如 QQ、微信、愛奇藝等很多知名度高的軟體,
- Objective-c/swift cocoa,這是 mac 平臺下的方案,可以方便呼叫底層的 API,缺點是不跨平臺,檔案不友好,UI 庫并不豐富,現在這種方式開發的越來越少了,
基于 Web 技術的桌面應用開發
從 B/S 和 C/S 架構逐漸融合的角度來說,基于 Web 技術進行桌面程式的開發漸漸變成了主流,因為對界面的代碼部分可以做到復用,
這類技術早期的方案是用 vb 內嵌 webBrowser 控制元件,基于 IE 內核,正好很多網頁開發也有用 activeX 的需求,但這種方式具有明顯的缺陷——非常依賴于用戶的環境,會因為組件缺失導致程式各種崩潰,第二類是嵌入式網頁框架,這類技術主要是基于瀏覽器引擎實作 UI 渲染,比較典型的就是 appkit 上面 UIWebView 和 CEF(chro-mium embeded framework),這種方法可以使用網頁 HTML5+CSS 實作各種酷炫的效果,但是缺點也比較明顯,就是桌面程式里面嵌入了一個類似 Chrome 的瀏覽器,記憶體的開銷會比較大,
后面出現了 nwjs 和 electron,electron 相比 CEF 有了單獨執行 js 的 v8 引擎,可以運行 NodeJS 來完成服務器端功能,通過和內部瀏覽器的 v8 引擎互動可以實作一個獨立的客戶端,這不同于 CEF 需要寄宿在其他程式內部,
Electron

用 Electron 來做桌面程式開發的優勢明顯,相當于是完全的網頁編程,有 Web 開發經驗的前端開發上手非常容易,Web 開發生態廣泛,開發成本低,可擴展性強,一些流行的前端框架例如 React、Angular、Vue 都可以和 electron 結合進行開發,另外它也具備和 Qt 一樣跨平臺的優良特性,對性能要求不高的桌面版程式來說,一份代碼同時得到網頁版和各個平臺的桌面版,開發的效率是其他方案無法比的,可以說,這是大部分人看好的趨勢,
和 Web 開發的區別
前端開發的一個痛點就是經常需要考慮多種瀏覽器之間的兼容,但是使用 electron 開發則不存在這樣的問題,它只需要考慮 electron 中對應 Chrome 的版本,另一個使用 electron 解決的痛點是跨域,它可以繞過客戶端直接通過 NodeJS 里的 request 通信模塊發出請求,這樣就無需被跨域所困擾,
擴展能力
node-ffi 可以在 NodeJS 環境中呼叫元件介面,通過這種方式,我們可以把 JavaScript 方法映射到元件介面,從而實作 C++ 類別庫的接入,
Electron 運行原理

Electron 的運行機制可以從兩種行程說起:主行程和渲染行程,運行 package.json 的稱為主行程,它可以負責創建渲染行程(多個),下圖是一段主行程代碼,主行程負責創建一個瀏覽器實體來加載網頁,每個創建的瀏覽器實體都在它自己的渲染行程內回傳一個 Web 頁面,當 BrowserWindow 實體銷毀時,相應的渲染行程也會終止,主行程負責掌管所有的 Web 頁面和它們相應的渲染行程,每個渲染行程都是相互獨立的,它們只關心自己所運行的 Web 頁面,
const electron = require('electron');const app = electron.app;const BrowserWindow = electron.BrowserWindow;var window = null;app.on('ready', function() { window = new BrowserWindow({width: 800, height: 600}); window.loadURL('<https://microstrategy.com>');});
復制代碼
主行程還負責應用程式的生命周期(app 打開退出)和一些 app 事件的監聽,同時負責系統底層 API 的呼叫,創建的渲染行程用來展示 HTML + CSS 技術撰寫的 Web 頁面, 同時可以運行 JavaScript 實作互動,可以把渲染行程簡單理解為 Chrome 上打開的一個瀏覽器行程,
<html> <body> <script> const remote = require('electron').remote; console.log(remote.app.getVersion()); </script> </body></html>
復制代碼
在渲染行程中是不允許呼叫原生 GUI 相關的 API,那是因為在網頁中掌管原生 GUI 很危險,易造成記憶體泄露,如果你想在網頁中進行 GUI 的操作,渲染行程必須向主行程傳達請求,然后在主行程中完成操作,
Electron 封裝了一系列自己的 API 可供主行程和渲染行程呼叫,可以通過如下方式獲取:
const {app, BrowserWindow, ipcMain, ... } = require('electron'); // 僅主行程可用const {ipcRender, remote, ... } = require('electron'); // 僅渲染行程可用
復制代碼
主行程和渲染行程均可直接呼叫 NodeJS 的 API:
node1.addEventListener('click', () => { console.log(os.path.basename('xxxx');})
復制代碼
行程通信
渲染行程如何向主行程發送訊息
通信本質上基于 nodeJS 的事件基礎類 Event-Emitter,ipcMain 和 ipcRender 都是該類的實體,通過事件模型需要的介面方法,采用了發布 / 訂閱的方式來發送和監聽訊息,
異步方式:
import { ipcRender } from 'electron'; // 在渲染行程引入 ipcRender,它是 EventEmitter 的一個實體ipcRender.send('async', 'I am from webview'); // 可以異步發送
復制代碼
同步方式:
import { ipcRender } from 'electron'; // 在渲染行程引入 ipcRenderipcRender.sendSync('sync', 'I am sync sent from webview'); // 同步方式
復制代碼
通過同步方式發送會 block 整個渲染行程,直到主行程回應,
主行程會加載 ipcMain 來進行監聽,
import { ipcMain } from 'electron'; // 在主行程引入 ipcMainipcMain.on('async', (event, info) { console.log(info);});
復制代碼
除此以外, electron 還提供了一種簡單的方案(remote 模塊)來實作渲染行程和主行程的通信,這樣就可以不必顯示地發送訊息,
import { remote } from 'electron'; // 在渲染行程引入 remoteremote.mainProcessObject.invokeMethod(); // 呼叫主行程才有的方法});
復制代碼
主行程如何向渲染行程發送訊息
主行程通過找到渲染行程對應的 browserWindow 就可以發送訊息,
constant window = BrowserWindow.fromId(global.id); // 通過 id 來進行對應window.webContents.send('msg', 'hello world');
復制代碼
渲染行程監聽:
ipcRender.on('msg', (event, info) => { console.log(info)});
復制代碼
渲染行程之間共享資料
一種可行的方案是在主行程實作訊息的轉發,還有比較簡單的方案是通過瀏覽器實作的 HTML API,比如 localStorage, sessionStorage 或者 IndexedDB,或者通過主行程創建全域變數 sharedObject 來實作渲染行程的資料共享,
以上是一些 electron 技術的初步介紹,有興趣的讀者可以繼續閱讀詳細的官方檔案來深入學習,目前來看 electron 不能算一個年輕的開源專案, 還有不少新的桌面替代技術在涌現(比如 miniblink),
作者介紹:
徐瑞青,高級軟體工程師,畢業于魯汶大學電子工程系,2014 年加入微策略,目前在資料 gateway 部門參與資料建模、清洗的開發作業,也參與過 Workstation 資料匯入的早期開發,
本文轉載自微信公眾號:微策略 商業智能
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/259700.html
標籤:其他
