上一篇介紹了 Electron 的架構,今天來了解一下它到底有什麼 API 供我們使用~
(圖片來源:Electronvue開發實戰1)
從圖可以了解,主進程、渲染進程都各自有可以使用的 API,而有些兩邊皆可以使用,因為這些功能實在是太多了,接下來就只介紹一些常用的 API
這邊介紹的 API 只有主進程能用,無法在渲染進程使用
app 操控整個應用程式的生命週期,常會用來監聽與整個應用程式的重要事件
// main.js
const { app } = require('electron')
app.on('window-all-closed', function () {
app.quit()
})
ready
:完成初始化時觸發window-all-closed
:所有視窗關閉時觸發activate
:喚醒應用程式時觸發(macOS)browser-window-created
:視窗被創建時觸發browser-window-focus
:視窗被關注時觸發browser-window-blur
:視窗被取消關注時觸發app.quit()
:關閉所有視窗app.exit()
:退出所有視窗,不會詢問用戶app.whenReady()
:初始化後執行(回傳 Promise
)app.hide()
:隱藏所有視窗(macOS)app.show()
:顯示所有視窗(macOS)app.requestSingleInstanceLock()
:視窗是否成功鎖定為單一視窗(回傳 Boolean
)// main.js
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock() // 鎖定視窗,並記錄回傳值
if (!gotTheLock) {
// 開啟第二個視窗時無法成功鎖定,關閉視窗
app.quit()
} else {
// 開啟第二個視窗時觸發,將第一個視窗還原並關注
// 此事件為第一個視窗發出
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// 正常開啟視窗,並儲存實例
app.whenReady().then(() => {
// 如果 createWindow 為 Promise,則需使用 than 或 async/await
myWindow = createWindow()
})
}
BrowserWindow 用來建立一個視窗,有許多外觀與功能的重要設定,也能監聽每個視窗的相關事件
// main.js
const { BrowserWindow } = require('electron')
const path = require('path')
// 以下沒特別註記即為預設值
const win = new BrowserWindow({
width: 800, // 寬度
height: 600, // 高度
x: 0, // 左方偏移量,預設置中
y: 0, // 上方偏移量,預設置中
useContentSize: false, // 設定窗口寬高是否包含框架與 Menu
minWidth: 0, // 最小寬度
minHeight: 0, // 最小高度
maxWidth: 1000, // 最大寬度,預設無限制
maxHeight: 1000, // 最大高度,預設無限制
resizable: true, // 可否調整視窗大小
movable: true, // 可否移動視窗
minimizable: true, // 可否最小化
maximizable: true, // 可否最大化
closable: true, // 可否點擊關閉按鈕
alwaysOnTop: false, // 視窗是否總是在頂部
fullscreen: false, // 視窗是否全螢幕顯示
fullscreenable: true, // 可否將視窗全螢幕
Kiosk: false, // 視窗是否開啟 Kiosk 模式
title: 'Electron', // 視窗標題
icon: './images/icon.png', // 視窗框架圖示
show: true, // 是否顯示視窗
frame: false, // 是否隱藏框架 與 Menu
parent: null, // 父視窗
disableAutoHideCursor: false, // 視窗內是否隱藏鼠標
autoHideMenuBar: false, // 是否隱藏 Menu(按下 Alt 可顯示)
backgroundColor: '#FFF', // 背景顏色
hasShadow: true, // 視窗是否有陰影
opacity: 0, // 視窗初始不透明度
darkTheme: false, // 視窗是否使用深色模式
transparent: false, // 視窗是否透明(frame 為 true 才有作用)
webPreferences: {
devTools: true, // 是否開啟 devtools
nodeIntegration: false, // 渲染進程是否可訪問 Node.js
preload: path.join(__dirname, 'preload.js'), // preload.js 文件位置
enableRemoteModule: false, // 是否啟用 remote 模塊
zoomFactor: 1.0, // 窗口縮放係數
webSecurity: true, // 是否開啟同源政策(關閉之後頁面才可以打 API)
webgl: true, // 是否啟用 WebGL
plugins: false, // 是否啟用插件
experimentalFeatures: false, // 是否啟用 Chromium 實驗功能
backgroundThrottling: true, // 是否在背景運行
}
})
win.loadURL('https://github.com')
// main.js
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.on('close', (e) => {
// do something...
})
close
:視窗將要關閉時觸發blur
:視窗失去焦點時觸發focus
:視窗獲得焦點時觸發show
:視窗顯示時觸發hide
:視窗隱藏時觸發ready-to-show
:視窗可顯示時觸發maximize
:視窗最大化時觸發unmaximize
:視窗退出最大化時觸發minimize
:視窗退出最小化時觸發restore
:視窗還原最小化時觸發will-resize
:調整視窗大小前觸發resize
:調整視窗大小時觸發resized
:調整視窗大小後觸發will-move
:移動視窗前觸發move
:移動視窗時觸發moved
:移動視窗後觸發enter-full-screen
:視窗進入全螢幕時觸發leave-full-screen
:視窗退出全螢幕後觸發// main.js
const { BrowserWindow } = require('electron')
BrowserWindow.getAllWindows()
BrowserWindow.getAllWindows()
:獲取一個包含所有視窗實例的陣列BrowserWindow.getFocusedWindow()
:獲取正在關注的視窗實例BrowserWindow.fromId(id)
:獲取指定 id 的視窗實例// main.js
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 800, height: 1500 })
win.destroy()
win.destroy()
:銷毀視窗,僅會發出 closed
事件win.close()
:關閉視窗win.show()
:顯示視窗win.hide()
:隱藏視窗win.maximize()
:視窗最大化win.minimize()
:視窗最小化win.restore()
:視窗還原最小化win.setFullScreen(Boolean)
:設定視窗是否全屏win.setSize(width, height)
:設定視窗寬高win.setContentSize(width, height)
:設定視窗寬高(不含框架與 Menu)win.moveTop()
:將視窗移至最上面win.center()
:將視窗移至中間win.setPosition(x, y)
:設定視窗位置win.setTitle(title)
:設定標題win.flashFrame(Boolean)
:設定視窗是否閃爍win.loadURL(url)
:視窗載入該 urlwin.loadFile(filePath)
:視窗載入該檔案win.reload()
:重新載入當前網頁這邊官方提到兩個開啟視窗的優化小技巧
// main.js
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ show: false })
win.once('ready-to-show', () => {
win.show()
})
// main.js
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ backgroundColor: '#2e2c29' })
win.loadURL('https://github.com')
webContents 跟 BrowserWindow
相當雷同,建立視窗後若需改變該視窗設定則可使用此方法,另外也有一些更細微的操作
// main.js
const { webContents } = require('electron')
webContents.getAllWebContents()
webContents.getAllWebContents()
:獲取一個所有視窗的 Array
webContents.getFocusedWebContents()
:獲取正在關注的視窗物件webContents.fromId(id)
:獲取指定 id 的視窗物件// main.js
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 800, height: 1500 })
win.webContents.loadURL('http://github.com')
win.webContents.loadURL(url)
:視窗載入該 urlwin.webContents.loadFile(filePath)
:視窗載入該檔案win.webContents.reload()
:重新載入當前網頁win.webContents.reloadIgnoringCache()
:重新載入當前網頁(忽略快取)win.webContents.getURL()
:獲取當前網頁網址win.webContents.getTitle()
:獲取當前網頁標題win.webContents.clearHistory()
:清除歷史紀錄win.webContents.goBack()
:上一頁win.webContents.goForward()
:下一頁win.webContents.goToIndex(index)
:導航到該索引索面win.webContents.getUserAgent()
:獲取此網頁用戶代理win.webContents.getZoomFactor()
:獲取當前縮放係數win.webContents.setZoomFactor(factor)
:設定縮放係數,原始為 1.0win.webContents.openDevTools()
:打開 devtoolswin.webContents.closeDevTools()
:關閉 devtoolswin.webContents.copy()
:複製win.webContents.cut()
:剪下win.webContents.paste()
:貼上win.webContents.undo()
:復原win.webContents.redo()
:重做win.webContents.capturePage([x, y, width, height])
:網頁截圖(回傳 Promise
)win.webContents.send(channel, ...args)
:同 ipcMain.send
win.webContents.startDrag({file, icon})
:操作拖動文件win.webContents.printToPDF(options)
:將網頁截圖存成 PDF// main.js
const { BrowserWindow } = require('electron')
const fs = require('fs') // 檔案操作
const path = require('path') // 路徑操作
const os = require('os') // 系統操作
const win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('http://github.com')
// loading 完成之後執行
win.webContents.on('did-finish-load', () => {
win.webContents.printToPDF({}).then(data => {
// 設定路徑為桌面上的 demo.pdf
const pdfPath = path.join(os.homedir(), 'Desktop', 'demo.pdf')
// 將檔案創建在該路徑
fs.writeFile(pdfPath, data, (error) => {
if (error) throw error
})
}).catch(error => {
// catch error
})
})
Menu 用來建立與設定選單,可直接建立上方工具列的選單,或是其他地方的內容選單
// main.js
const { Menu } = require('electron')
const isMac = process.platform === 'darwin'
// 設定 Menu 樣板(Windows 與 MacOS 分開設定)
const template = [
...(isMac ? [{
label: 'A-Menu',
submenu: [
{ role: 'quit' }
]
}] : [{
label: 'B-Menu',
submenu: [
{ role: 'undo' },
{ type: 'separator' },
{ label: '關閉', role: 'quit' }
]
}])
]
// 用樣板建立一個選單
const menu = Menu.buildFromTemplate(template)
// 設定應用程式頂部選單
Menu.setApplicationMenu(menu)
Menu.buildFromTemplate(template)
:建構一個選單實例Menu.setApplicationMenu(menu)
:設定頂部選單// main.js
const { ipcMain, BrowserWindow, Menu } = require('electron')
ipcMain.on('click-right', () => {
// 設定 Menu 樣板
const template = [
{
label: 'B-Menu',
submenu: [
{ role: 'undo' },
{ type: 'separator' },
{ label: '關閉', role: 'quit' }
]
}
]
// 用樣板建立一個選單
const menu = Menu.buildFromTemplate(template)
// 顯示彈出選單
menu.popup(BrowserWindow.getFocusedWindow())
})
menu.popup([options])
:顯示彈出選單menu.closePopup([browserWindow])
:關閉彈出選單menu.append(menuItem)
:將 menuItem
插入選單最後方menu.insert(pos, menuItem)
:將 menuItem
插入指定位置建立選單 template
可以參考官方文件
如果想要客製化自己的 Menu,可以將 Menu 隱藏,並自己使用刻一個,不過要讓自己的視窗可以拖拉記得加上 CSS 屬性 -webkit-app-region: drag
,但是不要覆蓋到功能按鈕,否則按鈕會失效
<div class="menu">
<div class="drag">
<img class="icon" src="./icon.png">
<span>title</span>
</div>
<button><img class="icon" src="./minimiz.png"></button>
<button><img class="icon" src="./maximiz.png"></button>
<button><img class="icon" src="./close.png"></button>
</div>
.drag {
-webkit-app-region: drag;
}
Tray 是右下角的圖示與功能,使用 Menu
建立選單,須於初始化後調用
// main.js
const { Tray } = require('electron')
const tray = new Tray()
tray.on('click', (e) => {
// do something...
})
click
:圖示選單被左鍵點擊時觸發right-click
:圖示選單被右鍵點擊時觸發double-click
:圖示選單被雙擊時觸發// main.js
const { app, Menu, Tray } = require('electron')
let tray = null
app.whenReady().then(() => {
// 設定 icon 路徑
const tray = new Tray('./images/icon.png')
// 設定選單樣板
const contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', click: () => { console.log('click') } },
{ label: 'Item2' },
{ label: 'Item3', type: 'radio', checked: true },
{ label: 'Item4', type: 'radio' }
])
// 右下角 icon 被 hover 時的文字
tray.setToolTip('This is my application.')
// 設定定應用程式右下角選單
tray.setContextMenu(contextMenu)
})
tray.destroy()
:銷毀圖示選單tray.setToolTip(toolTip)
:設定圖示選單被 hover 時的樣式tray.displayBalloon(options)
:設顯示一個通知(Windows)
icon
:圖示路徑iconType
:none
、info
、warning
、error
或 custom
,預設為 custom
title
:標題(必填)content
:內容(必填)largeIcon
:是否啟用大圖示(Boolean),預設為 true
noSound
:是否播放音效(Boolean),預設為 false
respectQuietTime
:勿擾模式不顯示通知(Boolean),預設為 false
tray.removeBalloon()
:移除通知(Windows)建立選單 template
可以參考官方文件
Notification 用來建立一個系統通知,使用 new Notification(options)
建立一個通知實例,須於初始化後調用
// main.js
const myNotification = new Notification({
title: '標題',
subtitle: '副標題',
body: '內容',
silent: true,
icon: './images/icon.png',
timeoutType: 'default'
})
title
:標題(必填)body
:內容(必填)subtitle
:副標題(macOS)silent
:是否發出音效(Boolean)icon
:圖示路徑timeoutType
:通知消失時間,值為 default
或 never
// main.js
app.whenReady().then(() => {
const myNotification = new Notification()
myNotification.on('show', (e) => {
// do something...
})
})
show
:通知顯示時觸發click
:通知被點擊時觸發close
:通知關閉時觸發// main.js
app.whenReady().then(() => {
const myNotification = new Notification({
title: '標題',
body: '內容'
})
myNotification.show()
})
Notification.show()
:顯示通知Notification.close()
:關閉通知通知這邊因為各系統都有很多毛,所以如果沒有顯示請參考官方文件
globalShortcut 用來自定義快捷鍵,須於初始化後調用
// main.js
const { app, globalShortcut } = require('electron')
app.whenReady().then(() => {
// 註冊快捷鍵 CommandOrControl + 0
const ret = globalShortcut.register('CommandOrControl+0', () => {
// do something...
})
// 返回 Boolean,用於檢查快捷鍵是否註冊成功
globalShortcut.isRegistered('CommandOrControl+0')
})
app.on('will-quit', () => {
// 取消註冊的快捷鍵 CommandOrControl + 0
globalShortcut.unregister('CommandOrControl+0')
// 取消所有註冊的快捷鍵
globalShortcut.unregisterAll()
})
globalShortcut.register(accelerator, callback)
:註冊快捷鍵globalShortcut.unregister(accelerator)
:註消快捷鍵globalShortcut.registerAll(accelerator, callback)
:一次註冊多個快捷鍵,accelerator
為陣列globalShortcut.unregisterAll()
:註消所有快捷鍵可定義的 accelerator
請參考官方文件
dialog 為彈出視窗,用來開啟檔案選擇視窗或訊息彈出視窗等等
// main.js
const { dialog } = require('electron')
dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] })
dialog.showOpenDialogSync(options)
:打開檔案的彈窗,等待選取檔案並返回dialog.showOpenDialog(options)
:打開檔案的彈窗(返回一個 Promise
)dialog.showSaveDialogSync(options)
:儲存檔案的彈窗,等待選取檔案並返回dialog.showSaveDialog(options)
:儲存檔案的彈窗(返回一個 Promise
)title
:標題defaultPath
:預設位置buttonLabel
:確定按鈕的文字filters
:可限制檔案類型{
filters: [
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
{ name: 'Custom File Type', extensions: ['as'] },
{ name: 'All Files', extensions: ['*'] }
]
}
properties
:其他功能,陣列裡面放以下字串
openFile
:可選擇文件openDirectory
:可選擇資料夾multiSelections
:可選擇多個// main.js
const { dialog } = require('electron')
dialog.showMessageBox({ type: 'info', title: '標題', message: '內容' })
dialog.showMessageBoxSync(options)
:訊息的彈窗,等待點選並返回dialog.showMessageBox(options)
:訊息的彈窗(返回一個 Promise
)dialog.showErrorBox(title, content)
:錯誤視窗type
:類型,none
、info
、error
、question
與 warning
buttons
:按鈕文字,一個裡面為字串的陣列,選擇後回傳索引defaultId
:預設選擇的按鈕索引title
:訊息標題message
:訊息內容detail
:訊息詳細文字checkboxLabel
:checkbox 的文字checkboxChecked
:checkbox 預設值icon
:顯示的圖示cancelId
:取消按鈕的索引數,Esc 時返回此索引我們如果設定視窗 alwaysOnTop
時會發現,視窗將 dialog
蓋住了,這時候可以用以下方法
// main.js
import { BrowserWindow, dialog } from 'electron'
// 創建另外一個 alwaysOnTop 的視窗,且不顯示於畫面
const win = new BrowserWindow({ show: false, alwaysOnTop: true })
// 在該視窗顯示 dialog,並在回傳後將視窗銷毀
dialog.showMessageBox(win, { type: 'info', title: '標題', message: '內容' }).then(() => {
win.destroy()
})
這邊介紹的 API 在主進程能用,在渲染進程也可以使用,可以將值寫入全域,以便在 renderer.js
使用
// preload.js
const { clipboard, shell } = require('electron')
window.clipboard = clipboard
window.shell = shell
clipboard 就是剪貼簿的功能,可記錄文字、HTML、圖片等等
// main.js
const { clipboard } = require('electron')
clipboard.availableFormats([ 'text/plain', 'text/html' ])
clipboard.writeText('範例字串', 'selection')
console.log(clipboard.readText('selection'))
clipboard.writeText(text)
:將文字寫入剪貼簿clipboard.readText()
:讀取剪貼簿的文字clipboard.writeHTML(markup)
:將 html 寫入剪貼簿clipboard.readHTML()
:讀取剪貼簿的 htmlclipboard.writeImage(image)
:將圖片寫入剪貼簿clipboard.readImage()
:讀取剪貼簿的圖片clipboard.writeRTF(text)
:將 RTF 寫入剪貼簿clipboard.readRTF()
:讀取剪貼簿的 RTFclipboard.availableFormats([type])
:定義剪貼簿可用格式clipboard.clear()
:清除剪貼簿內容clipboard.write(data)
:可一次寫入多筆資訊
text
:文字html
:htmlimage
:圖片rtf
:RTFclipboard.write({
text: 'test',
html: '<b>Hi</b>',
rtf: '{\\rtf1\\utf8 text}',
})
shell 可以使用預設的應用程式開啟檔案,常用在用瀏覽器開啟檔案
// main.js
const { shell } = require('electron')
shell.openExternal('https://github.com')
shell.showItemInFolder(fullPath)
:打開該文件所在目錄並選取該文件shell.openPath(path)
:使用預設方式打開該文件shell.openExternal(url)
:使用預設瀏覽器打開該網址shell.moveItemToTrash(fullPath)
:刪除該檔案shell.beep()
:播放提示音nativeImage 是一個包裝圖片的物件,Electron 內許多回傳圖片都是此類型
// main.js
const { BrowserWindow, Tray, clipboard } = require('electron')
// 以下返回的皆為 nativeImage 物件
const appIcon = new Tray('/Users/somebody/images/icon.png')
const win = new BrowserWindow({ icon: '/Users/somebody/images/window.png' })
const image = clipboard.readImage()
nativeImage.createFromPath(path)
:依路徑建立一個 nativeImage
物件nativeImage.createFromBitmap(buffer, options)
:依檔案建立一個 nativeImage
物件nativeImage.createFromBuffer(buffer, options)
:依檔案建立一個 nativeImage
物件nativeImage.createFromDataURL(dataURL)
:依 dataURL 建立一個 nativeImage
物件nativeImage.createFromPath(path)
:依路徑建立一個 nativeImage
物件image.toPNG(path)
:返回 Bufferimage.toJPEG(quality)
:返回 Buffer,quality 為 0 ~ 100image.toBitmap(path)
:返回 Bufferimage.toDataURL(path)
:返回 Stringimage.getBitmap(path)
:返回 Buffer這邊介紹的 API 僅在渲染進程能用,無法在主進程使用
remote 是很特別的方法,他的作用是為了讓渲染進程使用部分主進程的功能,使用方法如下
// main.js
const { BrowserWindow } = require('electron')
const path = require('path')
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
enableRemoteModule: true // 啟用 remote 模塊
}
})
win.loadURL('https://github.com')
// preload.js
const { dialog } = require('electron').remote
window.dialog = dialog
// renderer.js
window.dialog.showErrorBox('標題', '內容')
remote.require(module)
:載入相對路徑模塊remote.getCurrentWindow()
:回傳當前視窗實例emote.getCurrentWebContents()
:回傳當前視窗內容實例// preload.js
const foo = require('electron').remote.require('./foo')
require('electron').remote.getCurrentWindow().on('close', () => {
// do something...
})
讀文件真的太苦了