最近在開發一款桌面端應用,用到了Electron
和React
。
React
作為日常使用比較頻繁的框架,這裡就不詳細說明了,這裡主要是想通過幾篇文章讓大家快速上手Electron
以及與React
完美融合。
本篇是系列文章的第一篇,主要是給大家分享Electron
的一些概念,讓大家對Electron
有一個初步的認知。
先來了解一下什麼是Electron
吧,可能很多小夥伴還沒有聽過Electron
,相信很多小夥伴此時的表情是這樣的:
看下官網的自我介紹:
Electron
是一個可以使用 Web
技術如 JavaScript
、HTML
和 CSS
來建立跨平臺原生桌面應用的框架。藉助 Electron
,我們可以使用純 JavaScript
來呼叫豐富的原生 APIs
。
Electron
用 web
頁面作為它的 GUI
,而不是繫結了 GUI
庫的 JavaScript
。它結合了 Chromium
、Node.js
和用於呼叫作業系統本地功能的 APIs
(如開啟檔案視窗、通知、圖示等)。
上面這張圖很好的說明了Electron
的強大之處。
正因如此,現在已經有很多由Electron
開發的應用,比如Atom
、Visual Studio Code
等。我們可以在Apps Built on Electron看到所有由Electron
構建的專案。
快速開始
前面說了那麼多廢話,下面進入正題,帶大家用五分鐘(為什麼是五分鐘?我猜的 ? )的時間執行一個Electron
的Hello World
。
安裝
這一步很簡單:
npm install electron -g
第一個 Electron
應用
一個最簡單的 Electron
應用目錄結構如下:
hello-world/
├── package.json
├── main.js
└── index.html
package.json
的格式和 Node
的完全一致,並且那個被 main
欄位宣告的指令碼檔案是你的應用的啟動指令碼,它執行在主程式上。你應用裡的 package.json
看起來應該像:
{
"name": "hello-world",
"version": "0.1.0",
"main": "main.js"
}
建立main.js
檔案並新增如下程式碼:
const { app, BrowserWindow } = require("electron");
const isDev = require("electron-is-dev");
const path = require("path");
let mainWindow;
app.on("ready", () => {
mainWindow = new BrowserWindow({
width: 1024,
height: 680,
webPreferences: {
nodeIntegration: true,
// https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
enableRemoteModule: true,
},
});
// https://www.electronjs.org/docs/api/browser-window#event-ready-to-show
// 在載入頁面時,渲染程式第一次完成繪製時,如果視窗還沒有被顯示,渲染程式會發出 ready-to-show 事件 。 在此事件後顯示視窗將沒有視覺閃爍
mainWindow.once("ready-to-show", () => {
mainWindow.show();
});
const urlLocation = `file://${__dirname}/index.html`;
mainWindow.loadURL(urlLocation);
});
然後是index.html
檔案:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello World!</title>
<style media="screen">
.version {
color: red;
}
</style>
</head>
<body>
<h1>Hi! 我是柯森!</h1>
</body>
</html>
到這裡main.js
, index.html
和 package.json
這幾個檔案都有了。萬事俱備,來執行這個專案。因為前面已經全域性安裝了electron
,所以我們可以使用 electron
命令來執行專案。在 hello-world/
目錄裡面執行下面的命令:
$ electron .
你會發現會彈出一個 electron
應用客戶端,如圖所示:
到這裡,我們已經完成了一個最簡單的electron
應用。
但你一定會對上面用到的一些api
有疑惑,下面我將帶大家深入淺出的瞭解一下electron
的常用概念和api
。
相關概念
Electron
的程式分為主程式和渲染程式。在說這個之前,我覺得有必要先說一下程式和執行緒的概念。
程式和執行緒
這裡參考的是廖雪峰老師關於程式和執行緒概念的闡述,我覺得說的清晰明瞭。
對於作業系統來說,一個任務就是一個程式(Process
),比如開啟一個瀏覽器就是啟動一個瀏覽器程式,開啟一個記事本就啟動了一個記事本程式,開啟兩個記事本就啟動了兩個記事本程式,開啟一個Word
就啟動了一個Word
程式。
有些程式還不止同時幹一件事,比如Word
,它可以同時進行打字、拼寫檢查、列印等事情。在一個程式內部,要同時幹多件事,就需要同時執行多個“子任務”,我們把程式內的這些“子任務”稱為執行緒(Thread
)。
主程式和渲染程式
主程式
在 electron
裡面,執行 package.json
裡面 main
指令碼的程式被稱為主程式
。主程式控制整個應用的生命週期,在主程式中可以建立 Web
形式的 GUI
,而且整個 Node API
是內建其中。
渲染程式
由於 Electron
使用 Chromium
來展示頁面,所以 Chromium
的多程式架構也被充分利用。每個 Electron
的頁面都在執行著自己的程式,這樣的程式我們稱之為渲染程式
。
在一般瀏覽器中,網頁通常會在沙盒環境
下執行,並且不允許訪問原生資源。然而,Electron
使用者擁有與底層作業系統直接互動的能力。
主程式與渲染程式的區別
主程式使用BrowserWindow
例項建立頁面。每個BrowserWindow
例項都在自己的渲染程式裡執行頁面。當一個BrowserWindow
例項被銷燬後,相應的渲染程式也會被終止。
主程式管理所有頁面和與之對應的渲染程式。每個渲染程式都是相互獨立的,並且只關心他們自己的頁面。
在 electron
中,頁面不直接呼叫底層 APIs
,而是通過主程式進行呼叫。所以如果你想在網頁裡使用 GUI
操作,其對應的渲染程式必須與主程式進行通訊,請求主程式進行相關的 GUI
操作。
在 electron
中,主程式和渲染程式的通訊主要有以下幾種方式:
- ipcMain、ipcRender
- Remote 模組
程式通訊將稍後在下文詳細介紹。
BrowserWindow 的建立
BrowserWindow
用於建立和控制瀏覽器視窗。像上面的hello-world
中:
mainWindow = new BrowserWindow({
width: 1024,
height: 680,
webPreferences: {
nodeIntegration: true,
// https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
enableRemoteModule: true,
},
});
const urlLocation = `file://${__dirname}/index.html`;
mainWindow.loadURL(urlLocation);
建立了一個1024*680
的視窗,並通過loadURL
方法來載入了一個本地的html
檔案。
這裡一般會通過區分環境載入對應不同的檔案。
程式間的通訊
在計算機系統設計中,不同的程式間記憶體資源都是相互隔離的,因此程式間的資料交換,會使用程式間通訊方式達成。而不同於一般的原生應用開發,Electron
的渲染程式與主程式分別屬於獨立的程式中,而且程式間會存在頻繁的資料交換,這時選擇一個合理的程式間通訊方式顯得尤為重要。下面是 Electron
中官方提供的程式間通訊方式:
window.postMessage,LocalStorage
在前端開發中,鑑於瀏覽器對本地資料有嚴格的訪問限制,所以一般通過該兩種方式進行視窗間的資料通訊,該方式同樣適用於 Electron
開發中。然而因為 API
設計目的僅僅是為了前端視窗間簡單的資料傳輸,大量以及頻繁的資料通訊會導致應用結構鬆散,同時傳輸效率也值得懷疑。
使用IPC
進行通訊
Electron
中提供了 ipcRender
、ipcMain
作為主程式以及渲染程式間通訊的橋樑,該方式屬於 Electron
特有傳輸方式,不適用於其他前端開發場景。Electron
沿用 Chromium
中的 IPC
方式,不同於 socket
、http
等通訊方式,Chromium
使用的是命名管道 IPC
,能夠提供更高的效率以及安全性。
主程式收發資訊
詳細參考ipcMain
- 主程式接收渲染程式傳送的資訊
ipcMain.on("message", (e, msg) => {
console.log(msg);
});
- 主程式(主視窗)傳送資訊給渲染程式
mainWindow.webContents.send('message', { name: 'from the main by cosen' });
渲染程式收發資訊
通過ipcRenderer
傳送或接收
- 渲染程式接收主程式傳送的資訊
ipcRenderer.on("message", (e, msg) => {
console.log(msg);
});
- 渲染程式傳送資訊給主程式
ipcRenderer.send("message", { name: "Cosen" });
使用remote
實現跨程式訪問
remote
模組提供了一種在渲染程式(網頁)和主程式之間進行程式間通訊(IPC
)的簡便途徑。
Electron
中, 與GUI
相關的模組(如 dialog
, menu
等)只存在於主程式,而不在渲染程式中 。為了能從渲染程式中使用它們,需要用ipc
模組來給主程式傳送程式間訊息。使用 remote
模組,可以呼叫主程式物件的方法,而無需顯式地傳送程式間訊息。
總結
本小節我們大概的瞭解了Electron
的一些概念以及執行了一個入門的hello-world
程式。但這遠遠還不夠,下一節我會講一下如何將Electron
與React
完美融合,畢竟還是要更貼近業務的~
好了,不早了,我要去開啟我的網易雲時光了 ?
❤️ 愛心三連擊
1.如果覺得這篇文章還不錯,來個分享、點贊、在看三連吧,讓更多的人也看到~
2.關注公眾號前端森林,定期為你推送新鮮乾貨好文。
3.特殊階段,帶好口罩,做好個人防護。