摘要:當(dāng)一個實例被銷毀后,相應(yīng)的渲染進(jìn)程也會被終止。之所以命名為,主要是為了與主進(jìn)程這個概念對應(yīng)。部分在事件觸發(fā)后才能使用。當(dāng)全部窗口關(guān)閉時退出。主進(jìn)程接收到消息并處理之后,會返回處理結(jié)果。
簡介
Electron 是一個可以使用 Web 技術(shù)如 JavaScript、HTML 和 CSS 來創(chuàng)建跨平臺原生桌面應(yīng)用的框架。借助 Electron,我們可以使用純 JavaScript 來調(diào)用豐富的原生 APIs。
Electron用 web 頁面作為它的 GUI,而不是綁定了 GUI 庫的 JavaScript。它結(jié)合了 Chromium、Node.js 和用于調(diào)用操作系統(tǒng)本地功能的 APIs(如打開文件窗口、通知、圖標(biāo)等)。
現(xiàn)在已經(jīng)有很多由 Electron 開發(fā)應(yīng)用,比如 Atom、Insomnia、Visual Studio Code 等。查看更多使用 Electron 構(gòu)建的項目可以訪問 [Apps Built on Electron
](https://electron.atom.io/apps/)
安裝 electron 之前,需要安裝 Node.js。如果沒有安裝,推薦使用 nvm 等 Node.js 版本管理工具進(jìn)行安裝/
然后建議修改 electron 的源為國內(nèi)源:
$ export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
不然會出現(xiàn)如下錯誤:
Error: connect ETIMEDOUT 54.231.50.42:443 at Object.exports._errnoException (util.js:1016:11) at exports._exceptionWithHostPort (util.js:1039:20) at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1138:14)
安裝 electron:
$ npm install electron -g進(jìn)程
Electron 的進(jìn)程分為主進(jìn)程和渲染進(jìn)程。
主進(jìn)程在 electron 里面,運行 package.json 里面 main 腳本的進(jìn)程成為主進(jìn)程。主進(jìn)程控制整個應(yīng)用的生命周期,在主進(jìn)程中可以創(chuàng)建 Web 形式的 GUI,而且整個 Node API 是內(nèi)置其中。
渲染進(jìn)程每個 electron 的頁面都運行著自己的進(jìn)程,稱為渲染進(jìn)程。
主進(jìn)程與渲染進(jìn)程的聯(lián)系及區(qū)別主進(jìn)程使用 BrowserWindow 實例創(chuàng)建頁面。每個 BrowserWindow 實例都在自己的渲染進(jìn)程里運行頁面。當(dāng)一個 BrowserWindow 實例被銷毀后,相應(yīng)的渲染進(jìn)程也會被終止。
主進(jìn)程管理所有頁面和與之對應(yīng)的渲染進(jìn)程。每個渲染進(jìn)程都是相互獨立的,并且只關(guān)心他們自己的頁面。
在 electron 中,頁面不直接調(diào)用底層 APIs,而是通過主進(jìn)程進(jìn)行調(diào)用。所以如果你想在網(wǎng)頁里使用 GUI 操作,其對應(yīng)的渲染進(jìn)程必須與主進(jìn)程進(jìn)行通訊,請求主進(jìn)程進(jìn)行相關(guān)的 GUI 操作。
在 electron 中,主進(jìn)程和渲染進(jìn)程的通信主要有以下幾種方式:
ipcMain、ipcRender
Remote 模塊
進(jìn)程通信將稍后詳細(xì)介紹。
打造第一個 Electron 應(yīng)用以下所有代碼可以在 https://github.com/nodejh/electron-quick-start 找到。
一個最簡單的 electron 應(yīng)用目錄結(jié)構(gòu)如下:
electron-demo/ ├── package.json ├── main.js └── index.html
package.json 與 Node.js 的完全一致,所以我們可以使用 npm init 來生成。然后將 "main": "index.js" 修改為 "main": "main.js"。之所以命名為 main.js,主要是為了與主進(jìn)程這個概念對應(yīng)。
main.js創(chuàng)建 main.js 文件并添加如下代碼:
const electron = require("electron"); const { app, // 控制應(yīng)用生命周期的模塊 BrowserWindow, // 創(chuàng)建原生瀏覽器窗口的模塊 } = electron; // 保持一個對于 window 對象的全局引用,如果不這樣做, // 當(dāng) JavaScript 對象被垃圾回收, window 會被自動地關(guān)閉 let mainWindow; function createWindow() { // 創(chuàng)建瀏覽器窗口。 mainWindow = new BrowserWindow({width: 800, height: 600}); // 加載應(yīng)用的 index.html。 // 這里使用的是 file 協(xié)議,加載當(dāng)前目錄下的 index.html 文件。 // 也可以使用 http 協(xié)議,如 mainWindow.loadURL("http://nodejh.com")。 mainWindow.loadURL(`file://${__dirname}/index.html`); // 啟用開發(fā)工具。 mainWindow.webContents.openDevTools(); // 當(dāng) window 被關(guān)閉,這個事件會被觸發(fā)。 mainWindow.on("closed", () => { // 取消引用 window 對象,如果你的應(yīng)用支持多窗口的話, // 通常會把多個 window 對象存放在一個數(shù)組里面, // 與此同時,你應(yīng)該刪除相應(yīng)的元素。 mainWindow = null; }); } // Electron 會在初始化后并準(zhǔn)備 // 創(chuàng)建瀏覽器窗口時,調(diào)用這個函數(shù)。 // 部分 API 在 ready 事件觸發(fā)后才能使用。 app.on("ready", createWindow); // 當(dāng)全部窗口關(guān)閉時退出。 app.on("window-all-closed", () => { // 在 macOS 上,除非用戶用 Cmd + Q 確定地退出, // 否則絕大部分應(yīng)用及其菜單欄會保持激活。 if (process.platform !== "darwin") { app.quit(); } }); app.on("activate", () => { // 在 macOS 上,當(dāng)點擊 dock 圖標(biāo)并且該應(yīng)用沒有打開的窗口時, // 絕大部分應(yīng)用會重新創(chuàng)建一個窗口。 if (mainWindow === null) { createWindow(); } });
關(guān)于 app 和 BrowserWindow 對象和實例的更多用法可參考 electron 的文檔:
app
BrowserWindow
index.html然后編輯需要展示的 index.html:
Hello World! Hello World!
We are using Node.js and Electron
在這個例子中,我們顯示出了 electron 使用的 Node.js 版本和 electron 的版本。index.html 跟網(wǎng)頁的 HTML 一摸一樣,只是多了一些 electron 的全局對象。
運行因為前面已經(jīng)全局安裝了 electron,所以我們可以使用 electron 命令來運行項目。在 electron-demo/ 目錄里面運行下面的命令:
$ electron .
然后會彈出一個 electron 應(yīng)用客戶端,如圖所示:
因為在主進(jìn)程中啟用了開發(fā)模式 mainWindow.webContents.openDevTools(),所以默認(rèn)啟動開發(fā)者工具。
如果是局部安裝的 electron,即 npm install --save electron,則可以運行下面的命令來啟動應(yīng)用:
$ ./node_modules/.bin/electron .進(jìn)行通信
對于 electron 來說,主進(jìn)程和渲染進(jìn)程直接的通信是必不可少的。
前面提到過 electron 進(jìn)程間的通信的方式主要有兩種,一種是用于發(fā)送消息的 ipcMain 和 ipcRenderer 模塊,一種用于 RPC 的 remote 模塊。
現(xiàn)在假設(shè)一個業(yè)務(wù)場景,用戶在頁面中輸入文本消息,渲染進(jìn)程將消息發(fā)送給主進(jìn)程,主進(jìn)程處理后將處理結(jié)果返回給頁面。為了方便起見,主進(jìn)程的處理就假設(shè)為翻轉(zhuǎn)文本。當(dāng)然,這個功能在前端完全可以實現(xiàn),這里只是為了演示進(jìn)程通信。
ipcMain 和 ipcRenderer首先在渲染進(jìn)程中添加一個輸入框和一個按鈕,并實現(xiàn)點擊按鈕獲取輸入框的內(nèi)容。然后使用 ipcRenderer 發(fā)送消息。主進(jìn)程接收到消息并處理之后,會返回處理結(jié)果。所以渲染進(jìn)程中還需要接收主進(jìn)程的消息。
修改 index.html,添加下面的代碼:
接下來在主進(jìn)程中接收渲染進(jìn)程的消息,并進(jìn)行處理(翻轉(zhuǎn)字符串),然后將處理結(jié)果發(fā)送給主進(jìn)程。修改 main.js 如下:
//... // 監(jiān)聽渲染進(jìn)程發(fā)送的消息 ipcMain.on("asynchronous-message", (event, arg) => { const reply = arg.split("").reverse().join(""); console.log("reply: ", reply); // 發(fā)送消息到主進(jìn)程 event.sender.send("asynchronous-reply", reply); });
然后重新運行項目。在頁面的輸入框內(nèi)輸入字符,點擊按鈕,就能彈出如下的彈出框,說明渲染進(jìn)程與主進(jìn)程通信成功:
remoteremote 模塊提供了一種在渲染進(jìn)程(網(wǎng)頁)和主進(jìn)程之間進(jìn)行進(jìn)程間通訊(IPC)的簡便途徑。
使用 remote 模塊,我們可以很方便地調(diào)用主進(jìn)程對象的方法,而不需要發(fā)送消息。
在 index.html 的