Electron使用指南 - [03] Main Process API

Main Process API

Electron API (Electron API 有三種)
Main Process (主進進程)
Renderer Process(渲染進程)
Share Modules(共享模塊)

App

事件

ready:

當 Electron 完成初始化時被觸發。

  • 兩種使用方法
app.on('ready', createWindow)
app.on('ready', () => {
  console.log('App is ready!')
  createWindow()
})
  • 檢查應用是否登錄:app.isReady()
  • 如果應用沒有Ready,app.isReady()的值為 false
console.log('應用是否登錄:' + app.isReady())
  • 此時應用應該已經Ready
setTimeout(() => {
  console.log('應用是否登錄:' + app.isReady())
}, 2000)

before-quit

在應用程序開始關閉窗口之前觸發。

app.on('before-quit', (e) => {
  console.log('App is quiting')
  e.preventDefault()
})

browser-window-blur

在 browserWindow 失去焦點時發出

app.on('browser-window-blur', (e) => {
  console.log('App unfocused')
})

browser-window-focus

在 browserWindow 獲得焦點時發出

app.on('browser-window-focus', (e) => {
  console.log('App focused')
})

方法

app.quit()

app.on('browser-window-blur', (e) => {
  setTimeout(() => {
    app.quit()
  }, 3000)
})

app.on('browser-window-blur', (e) => {
  setTimeout(app.quit, 3000)
})

app.getPath(name)

app.on('ready', () => {
  console.log(app.getPath('desktop'))
  console.log(app.getPath('music'))
  console.log(app.getPath('temp'))
  console.log(app.getPath('userData'))

  createWindow()
})

BrowserWindow

electron.BrowserWindow: 創建和控制瀏覽器窗口

實例方法

win.loadURL(url[, options]): 和 loadFile 互斥

mainWindow.loadURL('https://www.baidu.com')

優雅的顯示窗口

  • 使用ready-to-show事件
let mainWindow = new BrowserWindow({ show: false })
mainWindow.once('ready-to-show', () => {
  mainWindow.show()
})
  • 設置 backgroundColor
let win = new BrowserWindow({ backgroundColor: '#2e2c29' })

父子窗口

  • 窗口定義
secondaryWindow = new BrowserWindow({
  width: 600,
  height: 600,
  webPreferences: { nodeIntegration: true }
})

secondaryWindow.loadFile('index.html')

secondaryWindow.on('closed',  () => {
   mainWindow = null
})
  • 窗口關系
secondaryWindow = new BrowserWindow({
  parent: mainWindon, // 定義父窗口
  modal: true // 鎖定在主窗口
})
  • 子窗口顯示和隱藏
secondaryWindow = new BrowserWindow({
  show: false
})

setTimeout(() => {
  secondaryWindow.show()
  setTimeout(() => {
    secondaryWindow.hide()
  }, 3000)
}, 2000)

無邊框窗口

Frameless Window

mainWindow = new BrowserWindow({
  frame: false
})

讓頁面可拖拽

<body style="user-select: none; -webkit-app-region:drag;">

no-drag 修復下面控件的bug

<input style="-webkit-app-region: no-drag;" type="range" name="range" min="0" max="10">

顯示紅綠燈

mainWindow = new BrowserWindow({
  titleBarStyle: 'hidden' // or hiddenInset 距離紅綠燈更近
})

屬性與方法

minWidth && minHeight

mainWindow = new BrowserWindow({
  minWidth: 300,
  minHeight: 300
})

更多詳見:https://electronjs.org/docs/api/browser-window#new-browserwindowoptions

窗口焦點事件

secWindow = new BrowserWindow({
  width: 400, height: 300,
  webPreferences: { nodeIntegration: true },
})

mainWindow.on('focus', () => {
  console.log('mainWindow focused')
})

secWindow.on('focus', () => {
  console.log('secWindow focused')
})

app.on('browser-window-focus', () => {
  console.log('App focused')
})

靜態方法

  • getAllWindows()
let allWindows = BrowserWindow.getAllWindows()
console.log(allWindows)

更多詳見: https://electronjs.org/docs/api/browser-window#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95

實例屬性

  • id
console.log(secWindow.id)

更多詳見:https://electronjs.org/docs/api/browser-window#%E5%AE%9E%E4%BE%8B%E5%B1%9E%E6%80%A7

實例方法

state

electron-window-state 保存窗口的狀態 npm install electron-window-state

webContents

webContents 是 EventEmitter 的實例, 負責渲染和控制網頁, 是 BrowserWindow 對象的一個屬性。
let wc = mainWindow.webContents console.log(wc)

方法 getAllWebContents()

  • 返回 WebContents[] - 所有 WebContents 實例的數組。 包含所有Windows,webviews,opened devtools 和 devtools 擴展背景頁的 web 內容const {app, BrowserWindow, webContents} = require('electron') console.log(webContents.getAllWebContents())

實例事件

  • did-finish-load
  • dom-ready
<div>
   <img src="https://placekitten.com/500/500" alt="">
</div>
<script>
let wc = mainWindow.webContents
wc.on('did-finish-load', () => {
  console.log('Conent fully loaded')
})
wc.on('dom-ready', () => {
  console.log('DOM Ready')
})
</script>
  • new-window
<div>
  <a target="_blank" ><h3>Kitten</h3></a>
</div>

<script>
wc.on('new-window', (e, url) => {
  e.preventDefault()
  console.log('DOM Ready')
})
</script>
  • before-input-event
wc.on('before-input-event', (e, input) => {
  console.log(`${input.key} : ${input.type}`)
})
  • login
  • did-navigate
mainWindow.loadURL('https://httpbin.org/basic-auth/user/passwd')

wc.on('login', (e, request, authInfo, callback) => {
  console.log('Logging in:')
  callback('user', 'passwd')
})

wc.on('did-navigate', (e, url, statusCode, message) => {
  console.log(`Navigated to: ${url}, with response code: ${statusCode}`)
  console.log(message)
})
  • media-started-playing
  • media-paused
<div>
  <video src="./mgm.mp4" controls width="400"></video>
</div>
<script>
wc.on('media-started-playing', () => {
  console.log('Video Started')
})
wc.on('media-paused', () => {
  console.log('Video Paused')
})
</script>
  • context-menu : 右鍵上下文信息
wc.on('context-menu', (e, params) => {
  console.log(`Context menu opened on: ${params.mediaType} at x:${params.x}, y:${params.y}`)
})

wc.on('context-menu', (e, params) => {
  console.log(`User seleted text: ${params.selectionText}`)
  console.log(`Selection can be copied: ${params.editFlags.canCopy}`)
})

實例方法

  • executeJavaScript()
wc.on('context-menu', (e, params) => {
  wc.executeJavaScript(`alert('${params.selectionText}')`)
})

Session

管理瀏覽器會話、cookie、緩存、代理設置等。

起步

  • 創建session對象
let session = mainWindow.webContents.session
console.log(session) // {}
  • 在chromium 創建localStorage,然后創建兩個窗口,兩個session共享
mainWindow = new BrowserWindow({
  width: 1000, height: 800,
  webPreferences: { nodeIntegration: true }
})

secWindow = new BrowserWindow({
  width: 500, height: 400,
  webPreferences: { nodeIntegration: true }
})

let session = mainWindow.webContents.session
let session2 = mainWindow.webContents.session
console.log(Object.is(session, session2)) // true

// Load index.html into the new BrowserWindow
mainWindow.loadFile('index.html')
secWindow.loadFile('index.html')

// Open DevTools - Remove for PRODUCTION!
mainWindow.webContents.openDevTools();
secWindow.webContents.openDevTools();

// Listen for window being closed
mainWindow.on('closed',  () => {
  mainWindow = null
})
secWindow.on('closed',  () => {
  secWindow = null
})
  • defaultSession
const {app, BrowserWindow, session} = require('electron')
let ses = mainWindow.webContents.session
console.log(Object.is(session.defaultSession, ses)) // true
  • 自定義session
let customSes = session.fromPartition('part1')
console.log(Object.is(customSes, ses)) //false, 此時customSes 還是共享session

secWindow = new BrowserWindow({
  width: 500, height: 400,
  webPreferences: { 
    nodeIntegration: true,
    session: customSes // 定義session, 此時子窗口有自己的session
  }
})

// 在子窗口里創建localstorge: winName/secWindow
// 關閉所有窗口,發現創建的localstorage又消失了,因為此時的session存儲在內存里,重新啟動應用又消失了。可以加前綴persist,使其變為永久存儲:

let customSes = session.fromPartition('persist:part1')

// 或者:

secWindow = new BrowserWindow({
  width: 500, height: 400,
  webPreferences: { 
    nodeIntegration: true,
    - session: customSes
    + partition: 'persist:part1'
  }
})
  • 實例方法
ses.clearStorageData() // 刪除主窗口的的storage

cookie

查詢和修改一個會話的cookies

// 查詢所有 cookies
session.defaultSession.cookies.get({})
  .then((cookies) => {
    console.log(cookies)
  }).catch((error) => {
    console.log(error)
  })
// 查詢所有與設置的 URL 相關的所有 cookies
session.defaultSession.cookies.get({ url: 'http://www.github.com' })
  .then((cookies) => {
    console.log(cookies)
  }).catch((error) => {
    console.log(error)
  })
// 設置一個 cookie,使用設置的名稱;
// 如果存在,則會覆蓋原先 cookie.
const cookie = { url: 'http://www.github.com', name: 'dummy_name', value: 'dummy' }
session.defaultSession.cookies.set(cookie)
  .then(() => {
    // success
  }, (error) => {
    console.error(error)
  })

downloadItem

控制來自于遠程資源的文件下載。

<h2><a  download>Download Image</a></h2>
<progress value="0" max="100" id="progress"></progress>

<script>
  window.progress = document.getElementById('progress')
</script>
// main.js
let ses = session.defaultSession

ses.on('will-download', (e, downloadItem, webContents) => {

  let fileName = downloadItem.getFilename()
  let fileSize = downloadItem.getTotalBytes()

  // Save to desktop
  downloadItem.setSavePath(app.getPath('desktop') + `/${fileName}`)

  downloadItem.on('updated', (e, state) => {

    let received = downloadItem.getReceivedBytes()

    if (state === 'progressing' && received) {
      let progress = Math.round((received/fileSize)*100)
      webContents.executeJavaScript(`window.progress.value = ${progress}`)
    }
  })
})

dialog - 對話框

顯示用于打開和保存文件、警報等的本機系統對話框

const {app, BrowserWindow, dialog} = require('electron')

mainWindow.webContents.on('did-finish-load', () => {
  dialog.showOpenDialog({
    buttonLabel: '選擇',
    defaultPath: app.getPath('desktop'),
    properties: ['multiSelections', 'createDirectory', 'openFile', 'openDirectory']
  }, filepaths => {
    console.log(filepaths)
  })
})
dialog.showSaveDialog({}, filename => {
  console.log(filename)
})
const answers = ['Yes', 'No', 'Maybe']

dialog.showMessageBox({
  title: 'Message Box',
  message: 'Please select an option',
  detail: 'Message details.',
  buttons: answers
}, response => {
  console.log(`User selected: ${answers[response]}`)
})

快捷鍵+系統快捷鍵

快捷鍵:定義鍵盤快捷鍵。 系統快捷鍵:在應用程序沒有鍵盤焦點時,監聽鍵盤事件。

快捷鍵可以包含多個功能鍵和一個鍵碼的字符串,由符號+結合,用來定義你應用中的鍵盤快捷鍵

示例:

  • CommandOrControl+A
  • CommandOrControl+Shift+Z

快捷方式使用 register 方法在 globalShortcut 模塊中注冊。

globalShortcut 模塊可以在操作系統中注冊/注銷全局快捷鍵, 以便可以為操作定制各種快捷鍵。

注意: 快捷方式是全局的; 即使應用程序沒有鍵盤焦點, 它也仍然在持續監聽鍵盤事件。 在應用程序模塊發出 ready 事件之前, 不應使用此模塊。

const {app, BrowserWindow, globalShortcut} = require('electron')

globalShortcut.register('G', () => {
  console.log('User pressed G')
})
globalShortcut.register('CommandOrControl+Y', () => {
  console.log('User pressed G with a combination key')
  globalShortcut.unregister('CommandOrControl+G')
})

Menu

1、index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>

    <textarea name="name" rows="8" cols="80"></textarea>

    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <strong><script>document.write( process.versions.node)</script></strong>,
    and Electron <strong><script>document.write( process.versions.electron )</script></strong>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>

2、main.js

// Modules
const {app, BrowserWindow, Menu, MenuItem} = require('electron')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

let mainMenu = Menu.buildFromTemplate( require('./mainMenu') )

// Create a new BrowserWindow when `app` is ready
function createWindow () {

  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { nodeIntegration: true }
  })

  // Load index.html into the new BrowserWindow
  mainWindow.loadFile('index.html')

  // Open DevTools - Remove for PRODUCTION!
  mainWindow.webContents.openDevTools();

  Menu.setApplicationMenu(mainMenu)

  // Listen for window being closed
  mainWindow.on('closed',  () => {
    mainWindow = null
  })
}

// Electron `app` is ready
app.on('ready', createWindow)

// Quit when all windows are closed - (Not macOS - Darwin)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow
app.on('activate', () => {
  if (mainWindow === null) createWindow()
})

3、mainMenu.js

module.exports = [
  {
    label: 'Electron',
    submenu: [
      { label: 'Item 1'},
      { label: 'Item 2', submenu: [ { label: 'Sub Item 1'} ]},
      { label: 'Item 3'},
    ]
  },
  {
    label: 'Edit',
    submenu: [
      { role: 'undo'},
      { role: 'redo'},
      { role: 'copy'},
      { role: 'paste'},
    ]
  },
  {
    label: 'Actions',
    submenu: [
      {
        label: 'DevTools',
        role: 'toggleDevTools'
      },
      {
        role: 'toggleFullScreen'
      },
      {
        label: 'Greet',
        click: () => { console.log('Hello from Main Menu') },
        accelerator: 'Shift+Alt+G'
      }
    ]
  }
]

Context Menus

1、index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>

    <textarea name="name" rows="8" cols="80"></textarea>

    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <strong><script>document.write( process.versions.node)</script></strong>,
    and Electron <strong><script>document.write( process.versions.electron )</script></strong>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>

2、main.js

// Modules
const {app, BrowserWindow, Menu} = require('electron')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

let contextMenu = Menu.buildFromTemplate([
  { label: 'Item 1' },
  { role: 'editMenu' }
])

// Create a new BrowserWindow when `app` is ready
function createWindow () {

  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { nodeIntegration: true }
  })

  // Load index.html into the new BrowserWindow
  mainWindow.loadFile('index.html')

  // Open DevTools - Remove for PRODUCTION!
  mainWindow.webContents.openDevTools();

  mainWindow.webContents.on('context-menu', e => {
    contextMenu.popup()
  })

  // Listen for window being closed
  mainWindow.on('closed',  () => {
    mainWindow = null
  })
}

// Electron `app` is ready
app.on('ready', createWindow)

// Quit when all windows are closed - (Not macOS - Darwin)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow
app.on('activate', () => {
  if (mainWindow === null) createWindow()
})

Tray (托盤)

1、main.js

// Modules
const {app, BrowserWindow, Tray, Menu} = require('electron')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow, tray

let trayMenu = Menu.buildFromTemplate([
  { label: 'Item 1' },
  { role: 'quit' }
])

function createTray() {

  tray = new Tray('trayTemplate@2x.png')
  tray.setToolTip('Tray details')

  tray.on('click', e => {

    if (e.shiftKey) {
      app.quit()
    } else {
      mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
    }
  })

  tray.setContextMenu(trayMenu)
}

// Create a new BrowserWindow when `app` is ready
function createWindow () {

  createTray()

  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { nodeIntegration: true }
  })

  // Load index.html into the new BrowserWindow
  mainWindow.loadFile('index.html')

  // Open DevTools - Remove for PRODUCTION!
  mainWindow.webContents.openDevTools();

  // Listen for window being closed
  mainWindow.on('closed',  () => {
    mainWindow = null
  })
}

// Electron `app` is ready
app.on('ready', createWindow)

// Quit when all windows are closed - (Not macOS - Darwin)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow
app.on('activate', () => {
  if (mainWindow === null) createWindow()
})

powerMonitor (電源指示器)

// Modules
const electron = require('electron')
const {app, BrowserWindow} = electron

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

// Create a new BrowserWindow when `app` is ready
function createWindow () {

  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { nodeIntegration: true }
  })

  // Load index.html into the new BrowserWindow
  mainWindow.loadFile('index.html')

  // Open DevTools - Remove for PRODUCTION!
  mainWindow.webContents.openDevTools();

  // Listen for window being closed
  mainWindow.on('closed',  () => {
    mainWindow = null
  })

  electron.powerMonitor.on('resume', e => {
    if(!mainWindow) createWindow()
  })

  electron.powerMonitor.on('suspend', e => {
    console.log('Saving some data')
  })
}

// Electron `app` is ready
app.on('ready', createWindow)

// Quit when all windows are closed - (Not macOS - Darwin)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// When app icon is clicked and app is running, (macOS) recreate the BrowserWindow
app.on('activate', () => {
  if (mainWindow === null) createWindow()
})
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容