簡介
Electron 是由Github開發,基於 Chromium 和 Node.js, 使用 JavaScript, HTML 和 CSS 等 Web 技術創建跨平臺原生桌面應用的框架。兼容Mac,Window和Linux,它構建的應用可在這三個操作系統上面運行,藉助 Electron,我們可以使用純 JavaScript 來調用豐富的原生 APIs。
現在已經有很多由 Electron 開發應用,比如 Atom、Insomnia、Visual Studio Code 等。查看更多使用 Electron 構建的項目可以訪問 Apps Built on Electron
安裝
Electron的開發需要安裝Node.js , npm , electron
Node.js安裝
下載安裝最新的版本即可。安裝完成之後打開命令行工具,輸入node -v,npm -v,可以看到如下顯示,說明node安裝成功。(npm是node的模塊管理工具,由node附帶安裝)
Electron安裝
npm切換到國內鏡像:
上面運行
npm install
安裝項目的依賴資源的時候,出現了報錯:”npm ERR!Windows_NT 6.1.7601”
報錯原因是:npm原本的鏡像資源索取代理地址默認是國外的 https://rubygems.org/,訪問受限連接超時導致報錯,有兩種解決方法:- 方法一:翻牆(並非長久之計,但可以索取到最新的資源);
- 方法二:將代理設置爲國內的地址,使用國內的npm鏡像,通常使用阿里雲提供的淘寶鏡像:https://npm.taobao.org/或者騰訊的鏡像:https://gems.ruby-china.org/,安裝package時使用代理地址:
npm install -g package --registry=https://registry.npm.taobao.org
- 1
這裏
package
是要安裝的模塊的名稱,--registry
用來指定鏡像索取地址。爲了不用每次安裝都指定一個地址,這裏我們直接安裝淘寶定製的
cnpm
修改如下://安裝cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org //使用cnpm安裝package,例如electron cnpm install -g electron
- 1
- 2
- 3
- 4
通過cnpm安裝electron
看到官方的引導,開始使用要用npm
安裝electron-prebuilt
,但是安裝的時候會提示electron-prebuilt
已改名爲electron
,而且這裏我們使用的是cnpm
,所以使用以下方式:# Install the `electron` command globally cnpm install -g electron # Install as a development dependency cnpm install electron --save-dev
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
執行第一個安裝指令會在系統盤下面的創建一個資源目錄這裏我的目錄是:
C:\Users\Administrator\.electron
,該目錄下多了兩個文件:electron-v1.6.6-win32-x64.zip
和SHASUMS256.txt-1.6.6
:electron -v 查看electron是否安裝成功
或者使用electron 命令可以看到如圖所示界面
起步案例:
github上提供了一個簡單的案例:electron-quick-start
根據官方引導,使用以下指令:# Clone this repository git clone https://github.com/electron/electron-quick-start # Go into the repository cd electron-quick-start # Install dependencies cnpm install # Run the app cnpm start
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
執行正常的輸出如下,並彈出Helloworld窗口:
E:\Electron\electron-quick-start-master>cnpm install √ Installed 1 packages √ Linked 146 latest versions √ Run 1 scripts Recently updated (since 2017-04-26): 4 packages (detail see file E:\Electron\ele ctron-quick-start-master\node_modules\.recently_updates.txt) √ All packages installed (154 packages installed from npm registry, used 5s, sp eed 137.76kB/s, json 165(247.32kB), tarball 509.67kB) E:\Electron\electron-quick-start-master>cnpm start > electron-quick-start@1.0.0 start E:\Electron\electron-quick-start-master > electron .
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這裏展示的界面其實就是electron-quick-start目錄下
index.html
的佈局界面。
安裝 electron 之前,需要安裝 Node.js。如果沒有安裝,推薦使用 nvm 等 Node.js 版本管理工具進行安裝/
然後建議修改 electron 的源爲國內源:
$ export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
不然會出現如下錯誤:
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
進程
Electron 的進程分爲主進程和渲染進程。
主進程
在 electron 裏面,運行 package.json 裏面 main 腳本的進程成爲主進程。主進程控制整個應用的生命週期,在主進程中可以創建 Web 形式的 GUI,而且整個 Node API 是內置其中。
渲染進程
每個 electron 的頁面都運行着自己的進程,稱爲渲染進程。
主進程與渲染進程的聯繫及區別
主進程使用 BrowserWindow 實例創建頁面。每個 BrowserWindow 實例都在自己的渲染進程裏運行頁面。當一個 BrowserWindow 實例被銷燬後,相應的渲染進程也會被終止。
主進程管理所有頁面和與之對應的渲染進程。每個渲染進程都是相互獨立的,並且只關心他們自己的頁面。
在 electron 中,頁面不直接調用底層 APIs,而是通過主進程進行調用。所以如果你想在網頁裏使用 GUI 操作,其對應的渲染進程必須與主進程進行通訊,請求主進程進行相關的 GUI 操作。
在 electron 中,主進程和渲染進程的通信主要有以下幾種方式:
- ipcMain、ipcRender
- Remote 模塊
進程通信將稍後詳細介紹。
打造第一個 Electron 應用
以下所有代碼可以在 https://github.com/nodejh/electron-quick-start 找到。
一個最簡單的 electron 應用目錄結構如下:
electron-demo/
├── package.json
├── main.js
└── index.html
package.json 與 Node.js 的完全一致,所以我們可以使用 npm init 來生成。然後將 "main": "index.js" 修改爲 "main": "main.js"。之所以命名爲 main.js,主要是爲了與主進程這個概念對應。
main.js
創建 main.js 文件並添加如下代碼:
const electron = require('electron');
const {
app, // 控制應用生命週期的模塊
BrowserWindow, // 創建原生瀏覽器窗口的模塊
} = electron;
// 保持一個對於 window 對象的全局引用,如果不這樣做,
// 當 JavaScript 對象被垃圾回收, window 會被自動地關閉
let mainWindow;
function createWindow() {
// 創建瀏覽器窗口。
mainWindow = new BrowserWindow({width: 800, height: 600});
// 加載應用的 index.html。
// 這裏使用的是 file 協議,加載當前目錄下的 index.html 文件。
// 也可以使用 http 協議,如 mainWindow.loadURL('http://nodejh.com')。
mainWindow.loadURL(`file://${__dirname}/index.html`);
// 啓用開發工具。
mainWindow.webContents.openDevTools();
// 當 window 被關閉,這個事件會被觸發。
mainWindow.on('closed', () => {
// 取消引用 window 對象,如果你的應用支持多窗口的話,
// 通常會把多個 window 對象存放在一個數組裏面,
// 與此同時,你應該刪除相應的元素。
mainWindow = null;
});
}
// Electron 會在初始化後並準備
// 創建瀏覽器窗口時,調用這個函數。
// 部分 API 在 ready 事件觸發後才能使用。
app.on('ready', createWindow);
// 當全部窗口關閉時退出。
app.on('window-all-closed', () => {
// 在 macOS 上,除非用戶用 Cmd + Q 確定地退出,
// 否則絕大部分應用及其菜單欄會保持激活。
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// 在 macOS 上,當點擊 dock 圖標並且該應用沒有打開的窗口時,
// 絕大部分應用會重新創建一個窗口。
if (mainWindow === null) {
createWindow();
}
});
關於 app 和 BrowserWindow 對象和實例的更多用法可參考 electron 的文檔:
appBrowserWindowindex.html
然後編輯需要展示的 index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
<style media="screen">
.version {
color: red;
}
</style>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js
<span id="version-node" class="version"></span>
and Electron
<span id="version-electron" class="version"></span>
<script type="text/javascript">
console.log('process: ', process);
var versionNode = process.version;
var versionElectron = process.versions['electron'];
document.getElementById('version-node').innerText = versionNode
document.getElementById('version-electron').innerText = versionElectron
</script>
</body>
</html>
在這個例子中,我們顯示出了 electron 使用的 Node.js 版本和 electron 的版本。index.html 跟網頁的 HTML 一摸一樣,只是多了一些 electron 的全局對象。
運行
因爲前面已經全局安裝了 electron,所以我們可以使用 electron 命令來運行項目。在 electron-demo/ 目錄裏面運行下面的命令:
$ electron .
然後會彈出一個 electron 應用客戶端,如圖所示:
因爲在主進程中啓用了開發模式 mainWindow.webContents.openDevTools(),所以默認啓動開發者工具。
如果是局部安裝的 electron,即 npm install --save electron,則可以運行下面的命令來啓動應用:
$ ./node_modules/.bin/electron .
進行通信
對於 electron 來說,主進程和渲染進程直接的通信是必不可少的。
前面提到過 electron 進程間的通信的方式主要有兩種,一種是用於發送消息的 ipcMain 和 ipcRenderer 模塊,一種用於 RPC 的 remote 模塊。
現在假設一個業務場景,用戶在頁面中輸入文本消息,渲染進程將消息發送給主進程,主進程處理後將處理結果返回給頁面。爲了方便起見,主進程的處理就假設爲翻轉文本。當然,這個功能在前端完全可以實現,這裏只是爲了演示進程通信。
ipcMain 和 ipcRenderer
首先在渲染進程中添加一個輸入框和一個按鈕,並實現點擊按鈕獲取輸入框的內容。然後使用 ipcRenderer 發送消息。主進程接收到消息並處理之後,會返回處理結果。所以渲染進程中還需要接收主進程的消息。
修改 index.html,添加下面的代碼:
<!-- 在 body 部分添加一個輸入框和按鈕 -->
<div>
<input type="text" id="message" name="" value="">
<br/>
<button type="button" id="button" name="button">click me</button>
</div>
<script type="text/javascript">
// ...
// 添加下面的代碼。
// 引入 ipcRenderer 模塊。
var ipcRenderer = require('electron').ipcRenderer;
document.getElementById('button').onclick = function () {
var message = document.getElementById('message').value;
// 使用 ipcRenderer.send 向主進程發送消息。
ipcRenderer.send('asynchronous-message', message);
}
// 監聽主進程返回的消息
ipcRenderer.on('asynchronous-reply', function (event, arg) {
alert(arg);
});
</script>
接下來在主進程中接收渲染進程的消息,並進行處理(翻轉字符串),然後將處理結果發送給主進程。修改 main.js 如下:
//...
// 監聽渲染進程發送的消息
ipcMain.on('asynchronous-message', (event, arg) => {
const reply = arg.split('').reverse().join('');
console.log('reply: ', reply);
// 發送消息到主進程
event.sender.send('asynchronous-reply', reply);
});
然後重新運行項目。在頁面的輸入框內輸入字符,點擊按鈕,就能彈出如下的彈出框,說明渲染進程與主進程通信成功:
remote
remote 模塊提供了一種在渲染進程(網頁)和主進程之間進行進程間通訊(IPC)的簡便途徑。
使用 remote 模塊,我們可以很方便地調用主進程對象的方法,而不需要發送消息。
在 index.html 的 <script> 標籤中添加如下代碼:
// 引入 remote 模塊
var remote = require('electron').remote;
// 獲取主進程中的 BrowserWindow 對象
var BrowserWindow = remote.BrowserWindow;
// 創建一個渲染進程
var win = new BrowserWindow({ width: 200, height: 150 });
win.loadURL('http://nodejh.com');
然後使用 ctr + r 組合鍵刷新應用,就會看到創建出的一個新窗口。
打包
Electron 應用開發完成之後,還需要將其打包成對應平臺的客戶端。常用的打包工具有 electron-packager 和 asar。
這裏以 electron-packager 爲例。首先全局安裝 electron-packager:
$ npm install electron-packager -g
然後在項目中安裝 electron:
$ npm install electron --save-dev
然後打包:
$ electron-packager . electron-demo
總結
本文首先對 electron 做了簡單的介紹,然後講解了 electron 進程的概念,其進程包括主進程和渲染進程。然後創建了一個簡單的 electron 應用,並通過實現一個簡單的應用場景,對 electron 進程間的通信做了實踐。總體來說,使用 electron 創建桌面客戶端的開發體驗跟寫 Node.js 和網頁差不多。但本文對內置模塊比如 app、ipcMain、ipcRender、remote 等的介紹比較粗淺,涉及到一些內置模塊的使用,還需要繼續查詢 electron 的官方文檔,只有實踐越多,才能越熟悉。