前言 Electron 是一個搭建跨平臺桌面應用的框架,僅僅使用 JavaScript、HTML 以及 CSS,即可快速而容易地搭建一個原生應用。這對於想要涉及其他領域的開發者來說是一個非常大的福利。 原文作者:林鑫,作者博客:http://blog.gdfengshuo.com/ 項目介紹 倉庫地 ...
前言
Electron 是一個搭建跨平臺桌面應用的框架,僅僅使用 JavaScript、HTML 以及 CSS,即可快速而容易地搭建一個原生應用。這對於想要涉及其他領域的開發者來說是一個非常大的福利。
原文作者:林鑫,作者博客:http://blog.gdfengshuo.com/
項目介紹
倉庫地址:lin-xin/calculator
我這裡通過 Electron 實現了仿 iPhone 的計算器,通過菜單可以切換橫屏和豎屏,橫屏有更多的運算。而對於 JavaScript 進行浮點數計算來說,精度丟失是個很大問題,所以我這裡使用了第三方庫 math.js 來解決這個精度的問題。
儘可能的實現了跟 iPhone 一樣的運算:
- 1 + 2 × 3 = 7
- 3 += 6 (再按 = 等於 9)
- 0.1 + 0.2 = 0.3 (浮點數精度處理)
不過我下麵並不是要講計算器,而是用到的 Electron 的知識點。
生命周期
在主進程中通過 app 模塊控制整個應用的生命周期。
當 Electron 完成初始化時觸發 ready 事件:
app.on('ready', () => {
// 創建視窗、載入頁面等操作
})
當所有的視窗都被關閉時會觸發 window-all-closed 事件:
app.on('window-all-closed', () => {
if(process.platform !== 'darwin'){
app.quit(); // 退出應用
}
})
在開發中發現,沒有監聽該事件,打包後的應用關閉後,進程還保留著,會占用系統的記憶體。
視窗
本來我們的 html 只顯示在瀏覽器中,而 electron 提供了一個 BrowserWindow 模塊用於創建和控制瀏覽器視窗,我們的頁面就是顯示在這樣的視窗中。
創建視窗
通過關鍵字 new 實例化返回 win 對象,該對象有豐富的方法對視窗進行控制。
win = new BrowserWindow({
width: 390, // 視窗寬度
height: 670, // 視窗高度
fullscreen: false, // 不允許全屏
resizable: false // 不允許改變視窗size,不然佈局就亂了啊
});
載入頁面
視窗創建完是一片空白的,可以通過 win.loadURL() 來載入要顯示的頁面。
const path = require('path');
const url = require('url');
win.loadURL(url.format({ // 載入本地的文件
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}))
也可以直接載入遠程鏈接 win.loadURL('http://blog.gdfengshuo.com');
菜單
桌面應用菜單欄是最常見的功能。Electron 提供了 Menu 模塊來創建原生的應用菜單和 context 菜單,
const template = [ // 創建菜單模板
{
label: '查看',
submenu: [
{label: '豎屏', type: 'radio', checked: true}, // type 屬性讓菜單為 radio 可選
{label: '橫屏', type: 'radio', checked: false},
{label: '重載',role:'reload'},
{label: '退出',role:'quit'},
]
}
]
const menu = Menu.buildFromTemplate(template); // 通過模板返回菜單的數組
Menu.setApplicationMenu(menu); // 將該數組設置為菜單
在子菜單中,通過點擊豎屏或橫屏來進行一些操作,那就可以給 submenu 監聽 click 事件。
const template = [
{
label: '查看',
submenu: [
{
label: '橫屏'
click: () => { // 監聽橫屏的點擊事件
win.setSize(670,460); // 設置視窗的寬高
}
}
]
}
]
主進程和渲染進程通信
雖然點擊橫屏的時候,可以設置視窗的寬高,但是要如何去觸發頁面里的方法,這裡就需要主進程跟渲染進程之間進行通信。
主進程,可以理解為 main.js 用來寫 electron api 的就是主進程,渲染進程就是渲染出來的頁面。
ipcMain
在主進程中可以使用 ipcMain 模塊,它控制著由渲染進程(web page)發送過來的非同步或同步消息。
const {ipcMain} = require('electron')
ipcMain.on('send-message', (event, arg) => {
event.sender.send('reply-message', 'hello world')
})
ipcMain 監聽 send-message 事件,當消息到達時可以調用 event.sender.send 來回覆非同步消息,向渲染進程發送 reply-message 事件,也可以帶著參數發送過去。
ipcRenderer
在渲染進程可以調用 ipcRenderer 模塊向主進程發送同步或非同步消息,也可以收到主進程的相應。
const {ipcRenderer} = require('electron')
ipcRenderer.on('reply-message', (event, arg) => {
console.log(arg); // hello world
})
ipcRenderer.send('anything', 'hello everyone');
ipcRenderer 可以監聽到來自主進程的 reply-message 事件並拿到參數進行操作,也可以使用 send() 方法向主進程發送消息。
webContents
webContents 是一個事件發出者,它負責渲染並控制網頁,也是 BrowserWindow 對象的屬性。在 ipcMain 中的 event.sender,返回發送消息的 webContents 對象,所以包含著 send() 方法用於發送消息。
const win = BrowserWindow.fromId(1); // fromId() 方法找到ID為1的視窗
win.webContents.on('todo', () => {
win.webContents.send('done', 'well done!')
})
remote
remote 模塊提供了一種在渲染進程(網頁)和主進程之間進行進程間通訊(IPC)的簡便途徑。在 Electron 中,有許多模塊只存在主進程中,想要調用這些模塊的方法需要通過 ipc 模塊向主進程發送消息,讓主進程調用這些方法。而使用 remote 模塊,則可以在渲染進程中調用這些只存在於主進程對象的方法了。
const {remote} = require('electron')
const BrowserWindow = remote.BrowserWindow // 訪問主進程中的BrowserWindow模塊
let win = new BrowserWindow(); // 其他的跟主進程的操作都一樣
remote 模塊除了可以訪問主進程的內置模塊,自身還有一些方法。
remote.require(module) // 返回在主進程中執行 require(module) 所返回的對象
remote.getCurrentWindow() // 返回該網頁所屬的 BrowserWindow 對象
remote.getCurrentWebContents() // 返回該網頁的 WebContents 對象
remote.getGlobal(name) // 返回在主進程中名為 name 的全局變數(即 global[name])
remote.process // 返回主進程中的 process 對象,等同於 remote.getGlobal('process') 但是有緩存
shell 模塊
使用系統預設應用管理文件和 URL,而且在主進程和渲染進程中都可以用到該模塊。在菜單中,我想點擊子菜單打開一個網站,那麼就可以用到 shell.openExternal() 方法,則會在預設瀏覽器中打開 URL
const {shell} = require('electron');
shell.openExternal('https://github.com/lin-xin/calculator');
打包應用
其實將程式打包成桌面應用才是比較麻煩的事。我這裡嘗試了 electron-packager 和 electron-builder。
electron-packager
electron-packager 可以將項目打包成各平臺可直接運行的程式,而不是安裝包。
先使用 npm 安裝: npm install electron-packager -S
運行打包命令:
electron-packager ./ 計算器 --platform=win32 --overwrite --icon=./icon.ico
打包會把項目文件包括 node_modules 也一起打包進去,當然可以通過 --ignore=node_modules 來忽略文件,但是如果項目中有用到第三方庫,忽略的話則找不到文件報錯了。
正確的做法就是嚴格區分 dependencies 和 devDependencies,打包的時候只會把 dependencies 的庫打包,而使用 cnpm 安裝的會有一大堆 .0.xx@xxx 的文件,也會被打包,所以最好不要用 cnpm
electron-builder
electron-builder 是基於 electron-packager 打包出來的程式再做安裝處理,將項目打包成安裝文件。
安裝:npm install electron-builder -S
打包:electron-builder --win
打包過程中,第一次下載 electron 可能會出現連接超時,可以使用 yarn 試試。還有 winCodeSign 和 nsis-resources 也可能會失敗,可以參考 electron-builder/issues 解決。
總結
Electron 用起來還是相對容易的,可以創建個簡單的桌面應用,只是打包的過程比較容易遇到問題,網上好像也有一鍵打包的工具,沒嘗試過。以上也都是基於 windows 7 的實踐,畢竟沒有 Mac 搞不了。