利用Electron構建桌面應用

只需五分鐘,就會Electronhtml

關於Electron

Electron Logo

Electron是由Github開發,用HTMLCSSJavaScript來構建跨平臺桌面應用程序的一個開源庫。 Electron經過將ChromiumNode.js 合併到同一個運行時環境中,打包出能夠運行在MacWindowsLinux系統下的應用。前端

核心理念:

  • 爲了保持Electron的小 (文件體積) 和可持續性 (依賴和API的擴展) ,Electron限制了使用的核心項目的範圍。
  • Electron只用了Chromium渲染庫 而不是 所有
  • Electron所添加的的新特性應主要用於原生API(node.js)。

Chromium是什麼?

Chromium是由Google主導開發的網頁瀏覽器,說明白點:node

Chromium是Chrome瀏覽器背後的引擎。react

幾個無聊的聯想

Chromium + Node.js = ?

若是把 瀏覽器Node.js 聯繫在一塊兒,通常想到的一個web服務,TCP以及其衍生協議充當了頁面服務器通訊的橋樑。linux

Web + Native = ?

瀏覽器能夠調用原生功能,很容易讓人聯想到Hybrid,web經過自定義的橋接協議與Native通訊,Native攔截請求從而得知web須要調用的功能。git

Electron的原理是什麼?

在一個Electron桌面應用分紅兩個進程:github

  • 主進程 —— MainProcess
  • 渲染進程 —— RenderProcess

主進程負責管理原生操做,例如: 文件讀寫,圖形處理,程序開關等,同時還管理渲染進程以及應用的生命週期。web

渲染進程經過 Chromium 繪製前端界面,用戶能夠在頁面上觸發事件。shell

那麼,Node.js 如何與Chromium通訊?npm

答案是 IPC——進程間通訊。

Electron IPC通訊模型

Electron IPC提供了基於事件的API,在渲染進程和後臺進程中均可以向對方發送事件,也能夠在事件處理函數中經過發送的新的事件回覆對方:

  • 經過EventEmitter實例向對方發送事件
  • 發送事件須要制定對方句柄,不支持全局廣播
  • 支持同步事件和異步事件

建立一個桌面應用

Step 1. 環境

  • Node.js = 8.x | 9.x

Step 2. 安裝electron包

npm install electron --save-dev --save-exact
複製代碼

若是下的慢,能夠嘗試淘寶鏡像

Step 3. 項目結構

.
└── app
  ├── main.js
  ├── index.js
  ├── index.html
  ├── package.json
複製代碼

main.js 是主進程執行文件, index.html 和 index.js 是渲染進程的視圖,和平時寫的網頁同樣。

須要在package.json寫明App名稱等信息,同時 須要指定入口文件(即main.js),舉個例子:

{
  "name": "app",
  "productName": "app",
  "version": "0.0.1",
  "main": "./app/index.js"
}
複製代碼

更詳細的範例能夠參照官方示例:electron-quick-start

Step 4. 啓動electron

主進程服務加上npm scripts:

"dev:electron-main": "cross-env NODE_ENV='development' electron -r babel-register ./",
複製代碼

啓動主進程:

npm run  dev:electron-main
複製代碼

Step 5. 開發

electron主進程文件修改以後須要重啓應用才能生效,每次手動重啓不方便,推薦使用鄙人抖機靈擼的一個包:

electron-watch

食用方法,在主進程文件中插入下面代碼片斷:

if (process.env.NODE_ENV === 'development') {
  require('electron-watch')(
    __dirname,
    'dev:electron-main', // means: npm run dev:electron-main
    path.join(__dirname, './'),
  );
}
複製代碼

** PS: 該庫Windows上有bug,正確的食用發放是隻按一次Ctrl+S,同時按幾回會重啓多個應用。 **


electron模塊

electron提供了豐富的API讓你調用原生的功能,參見electron文檔

可是過一下下面這幾個模塊,就能夠快速擼出一個Electron App。

1. app

一個桌面應用對象,提供API控制應用,同時能夠監控應用程序的事件生命週期。

例如在最後一個窗口被關閉時退出應用:

const {app} = require('electron');
app.on('window-all-closed', () => {
  app.quit();
});
複製代碼

能夠監控的生命週期事件:

  • will-finish-launching:當應用程序完成基礎的啓動的時候被觸發
  • ready: 當 Electron`: 完成初始化時被觸發
  • window-all-closed:當全部的窗口都被關閉時觸發
  • before-quit:在應用程序開始關閉窗口以前觸發
  • will-quit: 當全部窗口都已關閉而且應用程序將退出時發出
  • quit:在應用程序退出時發出

2. BrowserWindow

一個瀏覽器窗口

建立一個瀏覽器窗口:

import { app, BrowserWindow } from 'electron';

app.on('ready', () => {
  const win = new BrowserWindow({width: 800, height: 600});

  // 加載遠程URL
  win.loadURL('https://www.coolecho.net'); 

  // 或加載本地HTML文件
  win.loadURL(`file://${__dirname}/app/index.html`); 
});
複製代碼

父子窗口:

const {BrowserWindow} = require('electron');
  
const top = new BrowserWindow();
/** * 加上model屬性,子窗口爲模態窗口,父窗口被禁用 */
const child = new BrowserWindow({parent: top})
child.show()
top.show()
複製代碼

child 窗口將老是顯示在 top 窗口的頂部,長得像這個樣子:

window

3. ipcMain & ipcRender

ipcMainEventEmitter類的一個實例。 當在主進程中使用時,ipcRender處理從 渲染器進程(網頁) 發送出來的異步和同步信息。 從渲染器進程發送的消息將被髮送到該模塊。

ipcRenderer也是一個 EventEmitter 的實例。 你能夠使用它提供的一些方法從渲染進程 (web 頁面) 發送同步或異步的消息到主進程。 也能夠接收主進程回覆的消息。

下面是在渲染和主進程之間發送和處理消息的一個例子:

// 在主進程中
const {ipcMain} = require('electron');

// 異步
ipcMain.on('asynchronous-message', (event, arg) => {
    console.log(arg);  // prints "ping"
    event.sender.send('asynchronous-reply', 'pong');
});

// 同步
ipcMain.on('synchronous-message', (event, arg) => {
  console.log(arg)  // prints "ping"
  event.returnValue = 'pong'
});
複製代碼
//在渲染器進程 (網頁) 中
const {ipcRenderer} = require('electron');

// 同步請求
console.log(ipcRenderer.sendSync('synchronous-message', 'ping'));

// 異步發送請求
ipcRenderer.on('asynchronous-reply', (event, arg) => {
    console.log(arg) // prints "pong"
});
// 異步接收返回
ipcRenderer.send('asynchronous-message', 'ping');
複製代碼

你能夠經過ipcMainipcRenderer模塊,在渲染頁面中調用原生功能處理一些事物,固然electron作到的遠不止這些,它還提供了remote模塊。使用 remote 模塊, 你能夠調用 主進程對象 的方法, 而沒必要顯式發送進程間消息。

4. Menu

建立原生應用菜單和上下文菜單。

import { Menu } from 'electron';

const menuTemplete = [{
    label: 'File',
    submenu: [{
      label: 'New Note',
      accelerator: 'CmdOrCtrl+N',
      enabled: false,
      // role: 'new file',
      click: () => mainWindow.webContents.send('new-file', 1),
    }, {
      label: 'New Project',
      accelerator: 'Shift+CmdOrCtrl+N',
      enabled: false,
      // role: 'new project',
      click: () => mainWindow.webContents.send('new-project'),
    }, {
      type: 'separator',
    }, {
      label: 'Save',
      accelerator: 'CmdOrCtrl+S',
      role: 'save',
      enabled: false,
      click: () => mainWindow.webContents.send('save-content'),
    }],
 }];

const menu = Menu.buildFromTemplate(menuTemplete);

Menu.setApplicationMenu(menu);
複製代碼

效果:

menu

還能夠建立Context菜單:

const menu = new Menu();

menu.append(new MenuItem({
    label: 'Rename',
    click: () => mainWindow.webContents.send('rename-project'),
}));
menu.append(new MenuItem({
  label: 'Delete',
  click: () => mainWindow.webContents.send('delete-project'),
}));
menu.append(new MenuItem({
  type: 'separator',
}));
menu.append(new MenuItem({
  label: 'New Notebook', 
  click: () => mainWindow.webContents.send('new-project'),
}));

// 項目右鍵菜單
ipcMain.on('show-context-menu-project-item', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  menu.popup(win);
});
複製代碼

效果:

menucontext

菜單項

Electron提供了不少屬性來定製一個菜單項,例如上面例子中的labelclick,role等等:

  • click: 點擊菜單項的回調方法
  • role: Electron定製的內置事件,有click的時候,此項將被忽略
  • type: 能夠是 normalseparatorsubmenucheckboxradio
  • label: 菜單名稱,當設置role時默認爲role
  • accelerator: 定義快捷鍵
  • icon
  • sublabel
  • enabled: 若是爲 false,該菜單項將會置灰且不可點擊
  • visible: 控制菜單項是否可見
  • checked: 控制菜單項是否選中,typechckboxradio時有效
  • submenu: 定義子菜單項
  • id: 能夠經過它來引用該菜單項
  • position: 容許對給定菜單中的特定位置進行細粒度定義(沒試過)

其餘模塊

基本上知道上訴三個模塊,就能夠開發一個Electron APP了,但Electron的模塊遠遠不止這些,它還囊括網絡、電源、通知、進程、菜單、本地化、協議、會話、Shell等等模塊,詳細的介紹參閱Electron文檔


分發應用

應用開發完了,接下來得打包了。

有三種種打包方式:

  1. 手動打包
  2. 打包工具
  3. 經過重編譯源代碼來進行從新定製

1,3兩種方式有點繁瑣,這裏介紹幾個經常使用的打包工具:

若是想在APP STORE上架應用,,這需使用XCode填寫證書和開發者信息,手動打包。

我用的是electron-packer,打包起來十分方便,看看npm script

"packager:mac": "electron-packager ./lib Yosoro --overwrite --platform=darwin --arch=x64 --out=out --icon=assets/icons/osx/app.icns",
"packager:win": "electron-packager ./lib Yosoro --overwrite --platform=win32 --arch=ia32 --out=out --icon=assets/icons/win/app.ico",
"packager:linux": "electron-packager ./lib Yosoro --overwrite --platform=linux --arch=x64 --out=out",
複製代碼

--icon參數是用來指定應用的logo的,macOS下是.icns格式, Windows下是.ico格式。

爲啥Linux的沒有這個命令行參數呢?

由於Linux的Logo須要在初始化Electron應用的時候指定,格式爲PNGJPEG,建議使用PNG

const options = {
    title: 'Yosoro',
    width: 1180,
    height: 786,
};
if (process.platform === 'linux') { // 加上logo
  options.icon = path.join(__dirname, './resource/app.png');
}
mainWindow = new BrowserWindow(options);
複製代碼

options.icon這個屬性在macOSWindows下是無效的。

最後,你覺得直接npm run這三條命令就完事了嗎?

electron-packager只在對的地方幹對的事,例如,在macOS上只打macOS上運行的包,Windows上只打Windows上運行的包,從不會幹多餘的打包工做。好比在macOS下會直接打成一個APP包:

packager

若是你想分別打三端的包,須要分別在macOSWindowsLinux這三個環境下打包。

總結

關於Electron還有很是強大的功能,例如熱更新等等,本文只是菜雞隨手筆記,有錯誤的地方歡迎指正。

最後,關於Electron結合react的實例 戳這裏,求star。

相關文章
相關標籤/搜索