Electron簡介
Electron是干什么的? 簡單來講,Electron 使用 JavaScript,HTML 和 CSS,來構建跨平臺的桌面應用程式,
按照官方的說法:如果你可以建一個網站,你就可以建一個桌面應用程式,
和傳統的桌面應用相比,使用Electron開發更容易上手,開發效率更高,并且,web技術應用廣泛、生態繁榮,Electron可以使用幾乎所有的Web生態領域及Node.js生態領域的組件和技術方案,
與網頁應用相比,Electron基于Chromium 和 Node.js,可以避免令人頭痛的瀏覽器兼容問題,而Web前端受限訪問的檔案系統、系統托盤、系統通知等,開發Electron應用時可以自由地使用,
簡單理解Electron作業機制
使用Electron開發的桌面應用,類似于簡易版的、定制版的Chrome瀏覽器,當然這個瀏覽器中的頁面不能通過輸入網址打開,而是由開發者寫好的,

和瀏覽器架構類似,Electron應用程式區分主行程和渲染行程,
主行程負責控制應用程式的生命周期、創建和管理應用程式視窗,有著多種控制原生桌面功能的模塊,例如選單、對話框以及托盤圖示,
渲染行程負責完成渲染界面、接收用戶輸入、回應用戶的互動等作業,
一個Electron應用只有一個主行程,但可以有多個渲染行程,
在之前的文章中,我們有講過瀏覽器中的行程,可略作參考,
案例入門
下面我們將從一個任務管理的案例入門,了解electron的整體開發流程和一些基本的細節知識,
案例效果

功能分析:
1、記錄待完成任務和已完成任務
2、通過新建,添加待完成任務,并設定時間
3、點擊完成任務,跳轉到已完成界面;點擊洗掉,可以洗掉任務
4、點擊右上角的 × 按鈕,可以關閉主界面,要再次打開主界面,可以通過系統托盤
5、設定的時間到了,會在右下角彈出提醒框,如下圖所示,

初始化專案
專案是由原生js開發,在后面的文章中,我們會再探討electron和vue、react這些前端框架的結合,
mkdir tasky
cd tasky
npm init
安裝electron
npm install electron --S
創建一個hello world應用程式
(1)第一步,在專案根目錄下,創建index.js,作為應用程式的入口檔案,因為Electron是基于Node.js,所以入口檔案使用Node.js語法,內容如下:
//引入兩個模塊:app 和 BrowserWindow
//app 模塊,控制整個應用程式的事件生命周期,
//BrowserWindow 模塊,它創建和管理程式的視窗,
const { app, BrowserWindow } = require('electron')
//在 Electron 中,只有在 app 模塊的 ready 事件被激發后才能創建瀏覽器視窗
app.on('ready', () => {
//創建一個視窗
const mainWindow = new BrowserWindow()
//視窗加載html檔案
mainWindow.loadFile('./src/main.html')
})
(2)第二步,創建視窗需要加載的html檔案,
為了方便后面的檔案管理,我們新建一個 src 檔案夾,用于存放web頁面資源,比如html、css、js、圖片等,
// ./src/main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
hello world
</body>
</html>
(3)啟動程式,
修改package.json的scripts,如下:

然后在專案根目錄運行:npm run start,
這樣我們的hello world應用程式就跑起來了,入門Electron,就是這么簡單!

簡單的基礎除錯
1、主行程運行時的一些提示資訊會在命令列顯示,比如,在index.js加入console.log
app.on('ready', () => {
console.log('just test console.log')
const mainWindow = new BrowserWindow()
mainWindow.loadFile('./src/main.html')
})
就可以在命令列看到列印的值:

當index.js中的內容發生改變,默認要手動重啟,比較麻煩,這里我們加入nodemon,它可以監控node.js 源代碼的變化,并自動重啟應用,
安裝: npm i nodemon --D
修改scripts:"start": "nodemon --watch index.js --exec electron ."
再次運行npm run start,當index.js內容變化時,就會自動重新執行electron .來重啟應用,
2、視窗頁面的除錯方法和chrome瀏覽器類似,點擊選單欄的View --- Toggle Developer Tools,或者按它對應的快捷鍵,就會出現我們熟悉的開發者工具界面,

當頁面內容發生變化,可以點擊View --- Reload,或者快捷鍵ctrl+r,重繪頁面內容,頁面熱更新會后續講到,
開始coding
專案目錄結構如下,其中src檔案夾存放的就是web頁面相關的內容,

專案有兩個視窗:主視窗和提醒視窗,在主視窗中管理任務,當任務設定時間到,會在螢屏右下角出現提醒視窗,
應用不涉及服務端資料,任務資料主要使用localStorage來存盤,
創建主視窗:
const iconPath = path.join(__dirname, './src/img/icon.png') //應用運行時的標題欄圖示
let mainWindow
app.on('ready', () => {
mainWindow = new BrowserWindow({
resizable: false, //不允許用戶改變視窗大小
width: 800, //設定視窗寬高
height: 600,
icon: iconPath, //應用運行時的標題欄圖示
webPreferences:{
backgroundThrottling: false, //設定應用在后臺正常運行
nodeIntegration:true, //設定能在頁面使用nodejs的API
contextIsolation: false,
preload: path.join(__dirname, './preload.js')
}
})
mainWindow.loadURL(`file://${__dirname}/src/main.html`)
}
main.html的內容很簡單,有興趣的童鞋可以去看原始碼,這里就不貼了,

默認Electron應用頂部有標題欄和選單欄,縱觀各個桌面應用程式,基本都是定制的頂部控制區域,對于我們這個應用,暫時只要一個關閉按鈕,所以我們將去掉這一部分,將視窗關閉按鈕寫在main.html頁面中,
無邊框視窗
要創建無邊框視窗,只需在 BrowserWindow 的 options 中將 frame 設定為 false:
mainWindow = new BrowserWindow({
frame: false,
...
})
標題欄和選單欄消失了,但也會有幾個問題:
1、雖然選單欄消失了,但是依然可以通過快捷鍵進行選單操作,比如ctrl+shift+i打開開發者工具,為避免這種情況,我們需要去掉選單欄:
mainWindow.removeMenu()
2、默認情況下,無邊框視窗是不可拖拽的,應用程式需要在 CSS 中指定 -webkit-app-region: drag 來告訴 Electron 哪些區域是可拖拽的,
html,body {
height: 100%;
width: 100%;
}
body{
-webkit-app-region: drag;
}
如果用上面的屬性使整個視窗都可拖拽,則必須將其中的按鈕標記為不可拖拽,否則按鈕將無法點擊,
.enable-click {
-webkit-app-region: no-drag;
}
3、當點擊自定義的視窗關閉按鈕,我們并不希望退出程式,只是將視窗隱藏,可以通過系統托盤再次打開視窗,

系統托盤
程式啟動時,將應用程式加入系統托盤,在Electron中,借助Tray模塊實作,
const { app, BrowserWindow, Tray, Menu } = electron
const iconPath = path.join(__dirname, './src/img/icon.png')
let mainWindow, tray
app.on('ready', () => {
mainWindow = new BrowserWindow({
//... options
})
mainWindow.loadURL(`file://${__dirname}/src/main.html`)
tray = new Tray(iconPath) //實體化一個tray物件,建構式的唯一引數是需要在托盤中顯示的圖示url
tray.setToolTip('Tasky') //滑鼠移到托盤中應用程式的圖示上時,顯示的文本
tray.on('click', () => { //點擊圖示的回應事件,這里是切換主視窗的顯示和隱藏
if(mainWindow.isVisible()){
mainWindow.hide()
}else{
mainWindow.show()
}
})
tray.on('right-click', () => { //右鍵點擊圖示時,出現的選單,通過Menu.buildFromTemplate定制,這里只包含退出程式的選項,
const menuConfig = Menu.buildFromTemplate([
{
label: 'Quit',
click: () => app.quit()
}
])
tray.popUpContextMenu(menuConfig)
})
})
IPC通信
回到上一個問題,點擊頁面內的按鈕怎樣隱藏視窗?這就需要用到IPC通信了,

IPC(Inter-Process Communication),就是行程間通信,Electron應用程式區分主行程和渲染行程,有時候,兩者之間需要通信,傳輸一些資料、發送一些訊息,
渲染行程 TO 主行程
比如,點擊關閉按鈕,就需要渲染行程向主行程發送隱藏主視窗的請求,
渲染行程使用Electron內置的ipcRenderer模塊向主行程發送訊息,ipcRenderer.send方法的第一個引數是訊息管道名稱,
//頁面的js代碼:
const electron = require('electron')
const { ipcRenderer } = electron
closeDom.addEventListener('click', () => {
ipcRenderer.send('mainWindow:close')
})
主行程通過ipcMain接收訊息,ipcMain.on方法的第一個引數也為訊息管道的名稱,與ipcRenderer.send的名稱對應,第二個引數是接收到訊息的回呼函式,
//入口檔案index.js
ipcMain.on('mainWindow:close', () => {
mainWindow.hide()
})
主行程 TO 渲染行程
主行程向渲染行程發送訊息是通過渲染行程的webContents,在mainWindow渲染行程設定了任務后,會傳輸給主行程任務資訊,當任務時間到了,主行程會創建提醒視窗remindWindow,并通過remindWindow.webContents將任務名稱發給remindWindow,
function createRemindWindow (task) {
remindWindow = new BrowserWindow({
//options
})
remindWindow.loadURL(`file://${__dirname}/src/remind.html`)
//主行程發送訊息給渲染行程
remindWindow.webContents.send('setTask', task)
}
在remindWindow渲染行程中,通過ipcRenderer.on接受訊息,
ipcRenderer.on('setTask', (event,task) => {
document.querySelector('.reminder').innerHTML =
`<span>${decodeURIComponent(task)}</span>的時間到啦!`
})

渲染行程 TO 渲染行程
渲染行程之間傳遞訊息,可以通過主行程中轉,即視窗A先把訊息發送給主行程,主行程再把這個訊息發送給視窗B,這種非常常見,
也可以從視窗A直接發訊息給視窗B,前提是視窗A知道視窗B的webContents的id,
ipcRenderer.sendTo(webContentsId, channel, ...args)
值得注意的是,我們在頁面的js代碼中使用了require,這也是Electron的一大特點,在渲染行程中可以訪問Node.js API,這樣做的前提是在創建視窗時配置webPreferences的nodeIntegration: true和contextIsolation: false:
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration: true,
contextIsolation: false
}
})
視窗位置
當任務時間到,提醒視窗會在螢屏右下角出現,怎樣設定視窗位置呢?
function createRemindWindow (task) {
//創建提醒視窗
remindWindow = new BrowserWindow({
//...options
})
//獲取螢屏尺寸
const size = screen.getPrimaryDisplay().workAreaSize
//獲取托盤位置的y坐標(windows在右下角,Mac在右上角)
const { y } = tray.getBounds()
//獲取視窗的寬高
const { height, width } = remindWindow.getBounds()
//計算視窗的y坐標
const yPosition = process.platform === 'darwin' ? y : y - height
//setBounds設定視窗的位置
remindWindow.setBounds({
x: size.width - width, //x坐標為螢屏寬度 - 視窗寬度
y: yPosition,
height,
width
})
//當有多個應用時,提醒視窗始終處于最上層
remindWindow.setAlwaysOnTop(true)
remindWindow.loadURL(`file://${__dirname}/src/remind.html`)
}
關閉視窗
提醒視窗會在一段時間后關閉,可以通過remindWindow.close()來關閉視窗,
當視窗關閉后,我們可以設定remindWindow = null來回收分配給該渲染行程的資源,
remindWindow.on('closed', () => { remindWindow = null })
結語
這篇文章主要是介紹electron的一些基礎知識,下一篇文章,我們將探討electron的打包問題,下次見,
凡能用JavaScript實作的,注定會被用JavaScript實作,
---Jeff Atwood
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292752.html
標籤:其他
上一篇:手把手教你搭建應用的網路診斷模塊(1)——Ping與TraceRoute
下一篇:AIDL入門學習二
