乘風破浪,遇見跨平台桌面開發平台Electron - 使用JavaScript、HTML和CSS構建,比你想象的更簡單


什么是Electron

https://github.com/electron/electron

https://www.electronjs.org

Electron(最初名為Atom Shell)是GitHub開發的一個開源框架。它允許使用Node.js(作為后端)和Chromium(作為前端)完成桌面GUI應用程序的開發。Electron現已被多個開源Web應用程序用於前端與后端的開發,著名項目包括GitHub的Atom和微軟的Visual Studio Code。

image

Electron可以用於構建具有HTML、CSS、JavaScript的跨平台桌面應用程序,它通過將Chromium和Node.js合同一個運行的環境中來實現這一點,應用程序可以打包到Mac、Windows和Linux系統上。

image

Electron結合了Chromium Content Module和Node.js運行時。它允許開發人員使用網頁構建圖形用戶界面(GUI),以及通過與操作系統無關的API訪問OS X,Windows和Linux上的本機操作系統功能。

image

Chromium和Node本身都是廣受歡迎的應用程序平台,並且都已被獨立用於創建雄心勃勃的應用程序。Electron將這兩個平台結合在一起,允許您使用JavaScript來構建一類全新的應用程序。

你可以在瀏覽器中做的任何事情,你都可以用Electron做。任何你可以用Node做的事情,你可以用Electron做的任何事情。

我們可以一起構建利用這兩個平台的應用程序,並構建僅一個平台無法實現的應用程序。

起源

如果想開發一個桌面GUI應用軟件,希望其能同時在Windows、Linux和Mac平台上運行,可選的技術框架並不多,在早期人們主要使用以下三個框架來完成這類工作:

這三個框架都是用C/C++語言開發的,受語言開發效率的限制,開發者想通過它們快速地完成桌面應用的開發工作十分困難。

近幾年相繼出現了針對這些框架的現代編程語言綁定庫,諸如Python、C#、Go等,大部分都是開源社區提供的,但由於歷史原因,要想用到這些框架的全部特性,還是需要編寫C/C++代碼。並且由於幾乎沒有高質量的Node.js的綁定庫,前端程序員想通過這三個框架開發桌面應用更是難上加難。

Stack Overflow的聯合創始人JeffAtwood曾經說過:"凡能用JavaScript實現的,注定會被用JavaScript實現"。桌面GUI應用也不例外,近幾年兩個重量級框架NW.jsElectron橫空出世,給前端開發人員打開了這個領域的大門。

Electron最初由趙成和GitHub的工程師於2013年4月創建,當時名字為Atom Shell,用來服務於GitHub的開發工具Atom,2014年5月開源,2015年4月才正式更名為Electron。目前GitHub公司內部仍有一個團隊在維護這個開源項目,且社區內也有很多的貢獻者。

image

Electron更新非常頻繁,平均一到兩周就會有新版本發布,Issue和Pull request的回復也非常及時,一般關系到應用崩潰的問題(Crash Issue)一兩天就能得到回復,普通問題一周內也會有人跟進。社區活躍程度由此可見一斑。

image

與Electron類似的另外一個框架是NW.js,它們都是用Web前端技術來開發桌面GUI程序。

這兩個框架都與中國人有極深的淵源,2011年左右,中國英特爾開源技術中心的王文睿(Roger Wang)希望能用Node.js來操作WebKit,而創建了node-webkit項目,這就是NW.js的前身,但當時的目的並不是用來開發桌面GUI應用。

中國英特爾開源技術中心大力支持了這個項目,不僅允許王文睿分出一部分精力來做這個開源項目,還給了他招聘名額,允許他招聘其他工程師來一起完成。

image

2012年,故事的另一個主角趙成(Cheng Zhao)加入王文睿的小組,並對node-webkit項目做出了大量的改進。

后來趙成離開了中國英特爾開源技術中心,幫助GitHub團隊嘗試把node-webkit應用到Atom編輯器上,但由於當時node-webkit並不穩定,且node-webkit項目的走向也不受趙成的控制,這個嘗試最終以失敗告終。

image

但趙成和GitHub團隊並沒有放棄,而是着手開發另一個類似node-webkit的項目——Atom Shell,這個項目就是Electron的前身。趙成在這個項目上傾注了大量的心血,這也是這個項目后來廣受歡迎的關鍵因素之一。再后來GitHub把這個項目開源出來,最終更名為Electron

發展

2013年的時候,Atom編輯器問世,作為實現它的底層框架Electron也逐漸被熟知,到2014年春季被開源,那時它還是叫AtomShell。

接下來的幾年,Electron在不斷的更新迭代,幾乎每年都有一個重大的里程碑:

  • 2013年4月,Electron以Atom Shell為名起步。
  • 2014年5月,Atom以及Atom Shell以MIT許可證開源。
  • 2015年4月,項目被重命名為Electron。
  • 2016年5月11日,電子版發布v1.0.0版本。
  • 2016年5月20日,允許向Mac應用商店提交軟件包。
  • 2016年8月2日,支持Windows商店
  • 2018年5月2號發布的2.0.0

兼容

目前支持Electron的平台有OSXWindowsLinux

  • OSX:對於OSX系統僅有64位的二進制文檔,支持的最低版本是OSX 10.8。
  • Windows:僅支持Windows7及其以后的版本,之前的版本中是不能工作的。對於Windows提供x86和amd64(x64)版本的二進制文件。需要注意的是ARM版本的Windows目前尚不支持。
  • Linux:預編譯的ia32(i686)和x64(amd64)版本Electron二進制文件都是在Ubuntu12.04下編譯的,arm版的二進制文件是在ARMv7(硬浮點ABI與DebianWheezy版本的NEON)下完成的。預編譯二進制文件是否能夠運行,取決於其中是否包括了編譯平台鏈接的庫,所以只有Ubuntu12.04可以保證正常工作,但是Ubuntu12.04+、Fedora21、Debian8等平台也被證實可以運行Electron的預編譯版。

優劣

Electron的優點如下所示:

  • 部署升級方便,用戶可以通過瀏覽器就可以訪問。
  • HTML/JS/CSS編寫,方便且高效。
  • 可支持Windows、Linux、Mac系統。

Electron的缺點如下所示:

  • 對於開發者而言:瀏覽器適配比較繁瑣。有些應用必須指定瀏覽器版本(比如OCX必須是IE內核,H5必須是較高版本),必須打開瀏覽器,輸入一長串URL地址。
  • 對於用戶而言:傳統行業中部分用戶對web應用不習慣,尤其是使用專業工具軟件,大多數會覺得web應用沒有桌面應用用起來踏實。

現實

Electron在2014首次開源,作為一種使用Web技術(HTML+CSS+JS)構建桌面應用程序的方式,它迅速流行起來。其設計的核心思想是將可預測的環境捆綁在一起:

  • 它捆綁了自己的Chromium副本,因此,你可以確定你的HTML/CSS將如何被渲染,不必擔心IE瀏覽器(等)隨機的舊版本。
  • 它捆綁了自己的Node.js副本,因此,你擁有已知的一個可移植編程平台的版本,它超越了瀏覽器沙箱,可以直接與本機系統交互。

這些選擇在五年前很有價值,但到了2019年底,你可能會做出不同的選擇。這些選擇也是為什么Electron對資源極度渴求卻也會聞名的關鍵。默認的空白Electron 8.0.0應用程序需要下載164MB(壓縮后66MB),並作為4個單獨的進程運行,總共消耗150MB。

案例

Electron現已被多個開源應用軟件所使用,其中被廣大程序員所熟知和使用的Atom、支付寶小程序IDE、Visual Studio Code編輯器就是基於Electron實現的。

https://www.electronjs.org/apps

Visual Studio Code

打開Visual Studio Code,前往菜單"幫助"-"切換開發人員工具"

image

WhatsApp

image

Twitch

image

Microsoft Teams

image

Figma

image

VS Blazor

https://github.com/dotnet/maui/tree/main/src/BlazorWebView/src/Wpf

Blazor作為原生跨平台和Electron最大的區別就是真的是原生。以我的這個項目為例,用Blazor寫的代碼都是在WPF啟動進程里面跑的,只有當更新html的時候才會和webview2進行通訊,而此時也不是用的tcp,而是對edge webview2進行了一層封裝,所以完全沒有任何網絡請求。

基於此,我可以通過.NET訪問系統底層的任何東西,.NET生態幾乎所有的庫你都可以信手拈來,Debug也渾然天成,性能當然就是跟着.NET在Windows的性能走。在UI方面,我可以用html+css輕松地渲染各種效果,如果想,也可以加入js來進行互操作。好處顯而易見:.NET生態和web生態都可以比較好的融合,更小的限制,Visual Studio的支持,C#的項目還支持Hot Reload等等吧。

多進程架構

Electron繼承了來自Chromium的多進程架構,這使得此框架在架構上非常相似於一個現代的網頁瀏覽器。

image

網頁瀏覽器是個極其復雜的應用程序。除了顯示網頁內容的主要能力之外,他們還有許多次要的職責,例如:管理眾多窗口(或標簽頁)和加載第三方擴展

在早期,瀏覽器通常使用單個進程來處理所有這些功能。雖然這種模式意味着您打開每個標簽頁的開銷較少,但也同時意味着一個網站的崩潰或無響應會影響到整個瀏覽器

為了解決這個問題,Chrome團隊決定讓每個標簽頁在自己的進程中渲染,從而限制了一個網頁上的有誤或惡意代碼可能導致的對整個應用程序造成的傷害。然后用單個瀏覽器進程控制這些標簽頁進程,以及整個應用程序的生命周期。下方來自Chrome漫畫的圖表可視化了此模型:

image

Electron應用程序的結構非常相似。作為應用開發者,您控制着兩種類型的進程:主進程渲染器。這些類似於上面概述的Chrome自己的瀏覽器和其渲染器進程。

主進程

每個Electron應用都有一個單一的主進程,作為應用程序的入口點。主進程在Node.js環境中運行,這意味着它具有require模塊和使用所有Node.js API的能力。

  1. 窗口管理

主進程的主要目的是使用BrowserWindow模塊創建和管理應用程序窗口

BrowserWindow類的每個實例創建一個應用程序窗口,且在單獨的渲染器進程中加載一個網頁。您可從主進程用windowwebContent對象與網頁內容進行交互。

const { BrowserWindow } = require('electron')

const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('https://github.com')

const contents = win.webContents
console.log(contents)

注意:渲染器進程也是為web embeds而被創建的,例如BrowserView模塊。嵌入式網頁內容也可訪問webContents對象。

由於BrowserWindow模塊是一個EventEmitter,所以您也可以為各種用戶事件(例如,最小化或最大化您的窗口)添加處理程序。

當一個BrowserWindow實例被銷毀時,與其相應的渲染器進程也會被終止。

  1. 應用程序生命周期

主進程還能通過Electron的app模塊來控制您應用程序的生命周期。該模塊提供了一整套的事件和方法,可以使你添加自定義的應用程序行為(例如:以編程方式退出您的應用程序、修改程序塢或顯示關於面板)。

這是一個實際的例子,這個app來源於快速入門指南,用app API創建了一個更原生的應用程序窗口體驗。

// quitting the app when no windows are open on non-macOS platforms
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})
  1. 原生API

為了使Electron的功能不僅僅限於對網頁內容的封裝,主進程也添加了自定義的API來與用戶的作業系統進行交互。Electron有着多種控制原生桌面功能的模塊,例如菜單、對話框以及托盤圖標。

渲染器進程

每個Electron應用都會為每個打開的BrowserWindow(與每個網頁嵌入)生成一個單獨的渲染器進程。洽如其名,渲染器負責渲染網頁內容。所以實際上,運行於渲染器進程中的代碼是須遵照網頁標准的(至少就目前使用的Chromium而言是如此)。

因此,一個瀏覽器窗口中的所有的用戶界面和應用功能,都應與您在網頁開發上使用相同的工具和規范來進行攥寫

雖然解釋每一個網頁規范超出了本指南的范圍,但您最起碼要知道的是:

  • 以一個HTML文件作為渲染器進程的入口點。
  • 使用層疊樣式表(Cascading Style Sheets,CSS)對UI添加樣式。
  • 通過<script>元素可添加可執行的JavaScript代碼。

此外,這也意味着渲染器無權直接訪問require或其他Node.js API。為了在渲染器中直接包含NPM模塊,您必須使用與在web開發時相同的打包工具(例如webpack或parcel)

注意:渲染器進程可以生成一個完整的Node.js環境以便於開發。在過去這是默認的,但如今此功能考慮到安全問題已經被禁用。

此刻,您也許會好奇,您在渲染器進程中的用戶介面該如何與Node.js和Electron的原生桌面功能進行交互,如果這些功能都僅適用於主進程的話。而事實上,確實沒有直接導入Electron內容腳本的方法。

初探

https://github.com/TaylorShi/HelloElectron

准備環境NodeJS

開始創建或者編輯Electron項目之前,至少要准備好了現代前端都依賴的NodeJS,沒有的話,需要補裝一個。

通過Winget補裝

winget install 'OpenJS.NodeJS.LTS'

檢查已安裝的版本

node -v
npm -v

image

創建Electron應用

通其他NodeJS程序一樣,Electron應用也是需要先完成NPM包初始化操作。

創建應用目錄

mkdir HelloElectron

image

切換到應用目錄

cd .\HelloElectron\

image

使用VSC打開項目

code .

image

NPM包初始化

npm init

在初始化過程中,它會問你一些問題,對Electron應用來說,應用作者(Author)、應用描述(Description)建議填寫,另外應用入口(Entry Point)填寫main.js,其他的就看情況來了。

image

創建之后,我們會得到一個項目配置文件(Package.json),里面存放的就是我們剛剛回答的那些問題。

image

添加Electron包依賴

接下來,我們可以直接通過命令行,把Electron的包加到項目依賴中來。

npm install electron --save-dev --verbose

其中--save-dev代表僅作用於當前項目,而不是全局。然后--verbose可以顯示網絡請求的進度,方便查看進度。

image

如果網絡超時的話,可能需要開啟VPN,最后會得到一個成功的結果哈。

image

安裝完成后,我們會看到項目配置文件(Package.json)中添加了最新版本的Electron包依賴(v16.0.5)。

{
  "name": "helloelectronapp",
  "version": "0.0.1",
  "description": "creat first app for electron",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Taylor Shi",
  "license": "MIT",
  "devDependencies": {
    "electron": "^16.0.5"
  }
}

image

自定義調試

自定義調試命令

我們可以選擇在項目配置文件(Package.json)的指令腳本(scripts)中添加關於Electron的調試啟動命令。

{
    "scripts": {
        "start": "electron ."
    }
}

image

創建入口主文件(main.js)

在Electron應用程序中啟動入口都是從這個main.js開始的,這里我們先在根目錄建立一個空的main.js文件即可。

Main.js控制着主進程,它運行在一個完整的Node.js環境中,負責控制您應用的生命周期,顯示原生界面,執行特殊操作並管理渲染器進程。

image

通過命令啟動調試

npm start

image

因為當前的入口文件main.js是空的,所以這里不會有什么界面,但是不會報錯就是成功了。

添加應用頁面

在Electron中添加新頁面有很多種方式,其中一種就是可以直接加載本地的HTML文件。

  1. 出於演示需要,我們在本地建立一個靜態HTML文件(index.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
  </body>
</html>

image

  1. 將新頁面在窗口中打開

為了把這個頁面加載到當前應用窗口中來,我們需要依靠兩個Electron模塊,他們分別是:

  • app模塊,它控制應用程序的事件生命周期。
  • BrowserWindow模塊,它創建和管理應用程序窗口。

入口文件main.js中,我們在頭部將它們作為公共JS模塊進行導入。

const { app, BrowserWindow } = require('electron')

image

接下來,我們構建一個創建窗口(createWindow)方法把靜態HTML文件(index.html)加載進新的BrowserWindow實例中。

function createWindow () {
    const win = new BrowserWindow({
        width: 800,
        height: 600
    })

    win.loadFile('index.html')
}

image

然后我們通過app模塊中的ready事件來作為調用創建窗口方法的引信。通過app.whenReady方法我們可以監聽到這個事件,在它里面再去調用上訴的創建窗口的方法即可。

app.whenReady().then(() => {
    createWindow()
})

image

到此,完成最小演示所需的代碼就完畢了,快點,我們再來運行一次。

npm start

好可愛!它如我們所預期的那樣,出現了。

image

管理不同平台窗口生命周期

因為不同的系統平台可能存在一些應用程序窗口的差異性,那么我們需要考慮跨平台適配,通過進程(process)的平台(platform)屬性,我們可以為指定平台提供定制化策略。

  1. 關閉所有的窗口之后就要推出該應用程序(Windows&Linux)
app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') app.quit()
})

image

  1. 激活應用時無窗口打開就創建新窗口(MacOS)

對MacOS應用來說,應用可以在無窗口的情況下繼續運行,當應用被重新激活時,如果沒有窗口那么就直接創建新窗口即可。

可以通過監聽激活activate事件中補充策略,幫助在沒有任何可用窗口的情況下,創建新窗口,但是創建窗口必須在Ready事件之后才行。

app.whenReady().then(() => {
    createWindow()

    app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})

image

通過預加載讓渲染器讀取主進程數據

打包

添加Electron-Builder包

https://www.electron.build

npm install electron-builder --save-dev --verbose

image

配置應用信息

https://www.electron.build/?msclkid=167b1f41c78e11ecb237634483f909a0

我們需要在package.json文件里面,先完善好幾個已有的字段(如果沒有就添加上):

  • name 應用名稱
  • description 應用描述
  • version 應用版本號
  • author 發布者

比如:

{
    "name": "helloelectronapp",
    "version": "0.0.1",
    "description": "creat first app for electron",
    "author": "Taylor Shi"
}

配置構建信息

https://www.electron.build/configuration/configuration#configuration

接下來我們需要在package.json文件里面新增一個構建信息的build節點。

"build": {  // 這里是electron-builder的配置
    "productName":"xxxx",//項目名 這也是生成的exe文件的前綴名
    "appId": "com.xxx.xxxxx",//包名
    "copyright":"xxxx",//版權  信息
    "directories": { // 輸出文件夾
      "output": "build"
    }, 
    // windows相關的配置
    "win": {
      "icon": "xxx/icon.ico"//圖標路徑
    }
  }

其中:

  • appId 應用ID,默認也可以不定義。如果你要定義的話,不同生態就有不同的規矩,比如Mac就按CFBundleIdentifier來、Windows就按Application User Model ID

  • productName 產品名稱,默認也可以不定義。如果定義了,就會優先使用它,如果沒有定義就用外層的name參數值。

  • copyright 版權所有,默認也可以不定義。

例如:

"build":{
    "win":{
      "icon": "Assets/HelloElectron.ico"
    }
  }

添加打包命令

package.json文件里面的scripts節點中,增加打包指令。

"scripts": {
  "pack": "electron-builder --dir",
  "dist": "electron-builder"
}

例如:

 "scripts": {
    "start": "electron .",
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },

基於Electron-Builder打包

只是為了輸出二進制,使用命令:

npm run pack

image

最終輸入得到:

image

總體積:185MB

輸出二進制並且得到安裝包,使用命令:

npm run dist

image

最終輸入得到:

image

總體積:57.6MB

在整個過程中會去下載一些東西,有些是放在github上的,如果下載失敗,多試幾次,並且開流量代理去試驗。

玩一玩安裝。

image

安裝完還自動運行。

image

進階

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM