【Electron Playground 系列】窗口篇


作者:Kurosaki

本文主要講解Electron 窗口的 API 和一些在開發之中遇到的問題。

官方文檔 雖然比較全面,但是要想開發一個商用級別的桌面應用必須對整個 Electron API  有較深的了解,才能應對各種需求。

1. 創建窗口

通過BrowserWindow,來 創建 或者 管理 新的瀏覽器窗口,每個瀏覽器窗口都有一個進程來管理。

1.1. 簡單創建窗口

const { BrowserWindow } = require('electron');
const win = new BrowserWindow();
win.loadURL('https://github.com');

效果如下:

open-windows.gif

1.1.2. 優化

問題electron BrowserWindow 模塊在創建時,如果沒有配置 show:false,在創建之時就會顯示出來,且默認的背景是白色;然后窗口請求 HTML,會出現視覺閃爍。

解決

const { BrowserWindow } = require('electron');
const win = new BrowserWindow({ show:false });

win.loadURL('https://github.com');

win.on('ready-to-show',()=>{
    win.show();
})

兩者對比有很大的區別
window-shows.gif

1.2. 管理窗口

所謂的管理窗口,相當於主進程可以干預窗口多少。

  • 窗口的路由跳轉
  • 窗口打開新的窗口
  • 窗口大小、位置等
  • 窗口的顯示
  • 窗口類型(無邊框窗口、父子窗口)
  • 窗口內 JavaScript 的 node 權限,預加載腳本等
  • ....

這些個方法都存在於BrowserWindow模塊中。

1.2.1. 管理應用創建的窗口

BrowserWindow模塊在創建窗口時,會返回 窗口實例,這些 **窗口實例 **上有許多功能方法,我們利用這些方法,管理控制這個窗口。

在這里使用Map對象來存儲這些 窗口實例

const BrowserWindowsMap = new Map<number, BrowserWindow>()
let mainWindowId: number;

const browserWindows = new BrowserWindow({ show:false })
browserWindows.loadURL('https://github.com')
browserWindows.once('ready-to-show', () => {
  browserWindows.show()
})
BrowserWindowsMap.set(browserWindow.id, browserWindow)
mainWindowId = browserWindow.id  // 記錄當前窗口為主窗口

窗口被關閉,得把Map中的實例刪除。

browserWindow.on('closed', () => {
  BrowserWindowsMap?.delete(browserWindowID)
})

1.2.2. 管理用戶創建的窗口

主進程可以控制窗口許多行為,這些行為會在后續文章一一列舉;以下以主進程控制窗口建立新窗口的行為為例。

使用new-window監聽新窗口創建

// 創建窗口監聽
browserWindow.webContents.on('new-window', (event, url, frameName, disposition) => {
  /** @params {string} disposition
  *  new-window : window.open調用
  *  background-tab: command+click
  *  foreground-tab: 右鍵點擊新標簽打開或點擊a標簽target _blank打開
  * /
})

注:關於disposition字段的解釋,移步electron文檔electron源碼chrome 源碼

擴展new-window

經過實驗,並不是所有新窗口的建立, new-window 都能捕捉到的。

以下方式打開的窗口可以被new-window事件捕捉到

window.open('https://github.com')
<a href='https://github.com' target='__blank'>鏈接</a>

**
渲染進程中使用BrowserWindow創建新窗口,不會被 new-window事件捕捉到
**

const { BrowserWindow } = require('electron').remote
const win = new BrowserWindow()
win.loadURL('https://github.com')

_渲染進程訪問 __remote_ _,主進程需配置enableRemoteModule:true _
使用這種方式同樣可以打開一個新的窗口,但是主進程的new-window捕捉不到。

應用new-window
new-window 控制着窗口新窗口的創建,我們利用這點,可以做到很多事情;比如鏈接校驗、瀏覽器打開鏈接等等。默認瀏覽器打開鏈接代碼如下:

import { shell } from 'electron'
function openExternal(url: string) {
  const HTTP_REGEXP = /^https?:\/\//
  // 非http協議不打開,防止出現自定義協議等導致的安全問題
  if (!HTTP_REGEXP) {
    return false
  }
  try {
    await shell.openExternal(url, options)
    return true
  } catch (error) {
    console.error('open external error: ', error)
    return false
  }
}
// 創建窗口監聽
browserWindow.webContents.on('new-window', (event, url, frameName, disposition) => {
  if (disposition === 'foreground-tab') {
      // 阻止鼠標點擊鏈接
      event.preventDefault()
      openExternal(url)
  }
})

_關於 __shell_ 模塊,可以查看官網 https://www.electronjs.org/docs/api/shell
_

1.3. 關閉窗口

**close** **事件和 ****closed** 事件
close 事件在窗口將要關閉時之前觸發,但是在 DOM 的 beforeunload 和 unload 事件之前觸發。

// 窗口注冊close事件
win.on('close',(event)=>{
	event.preventDefault()  // 阻止窗口關閉
})

closed 事件在窗口關閉后出觸發,但是此時的窗口已經被關閉了,無法通過 event.preventDefault() 來阻止窗口關閉。

win.on('closed', handler)

主進程能夠關閉窗口的 API 有很多,但都有各自的利弊。

1.3.1. win.close()

關於這個 API 的利弊

  1. 如果當前窗口實例注冊並阻止close事件,將不會關閉頁面,而且也會 阻止計算機關閉(必須手動強制退出);
  2. 關閉頁面的服務,如websocket,下次打開窗口,窗口中的頁面會 重新渲染
  3. 通過這個API觸發的close事件在 unloadbeforeunload之前觸發,通過這點可以實現 關閉時觸發彈窗

window-close.gif
完整代碼在github:electron-playground

  1. 會被closed事件捕捉到。

1.3.2. win.destroy()

  1. 強制退出,無視close事件(即:無法通過event.preventDefault()來阻止);
  2. 關閉頁面,以及頁面內的服務,下次打開窗口,窗口中的頁面會重新渲染;
  3. 會被closed事件捕捉到。

1.3.3. win.hide()

這個隱藏窗口。

  1. 隱藏窗口,會觸發hideblur事件,同樣也是可以通過event.preventDefault()來阻止
  2. 只是隱藏窗口,通過win.show(),可以將窗口顯現,並且會保持原來的窗口,里面的服務也不會掛斷

2. 主窗口隱藏和恢復

2.1. 主窗口

2.1.1. 為什么需要 主窗口?

一個應用存在着許多的窗口,需要一個窗口作為 主窗口,如果該窗口關閉,則意味着整個應用被關閉。
場景:在應用只有一個頁面的時,用戶點擊關閉按鈕,不想讓整個應用關閉,而是隱藏;
例如:其他的APP,像微信,QQ等桌面端。

利用上文中提到的關閉窗口的 API ,我們實現一個主窗口的隱藏和恢復。

改造一下 close 事件

let mainWindowId: number // 用於標記主窗口id

const browserWindow = new BrowserWindow()

// 記錄下主窗口id
if (!mainWindowId) {
  mainWindowId = browserWindow.id
}

browserWindow.on('close', event => {
  // 如果關閉的是主窗口,阻止
  if (browserWindow.id === mainWindowId) {
    event.preventDefault()
    browserWindow.hide()
  }
})

2.1.2. 恢復主窗口顯示

能隱藏,就能恢復。

const mainWindow = BrowserWindowsMap.get(mainWindowId)
if (mainWindow) {
  mainWindow.restore()
  mainWindow.show()
}

**mainWindow.show()** 方法:功能如其名,就是“show出窗口”。
_為什么要是有 __mainWindow.restore()_ 
_windows_ _下如果 __hide_ _之后不調用 __show_ _方法而是只調用 __restore_ 方法就會導致頁面掛住不能用

2.1.3. 強制關閉主窗口

有些場景下,可能需要的強制退出,附上代碼:

const mainWindow = BrowserWindowsMap.get(mainWindowId)
if (mainWindow) {
  mainWindowId = -1
  mainWindow.close()
}

存在的問題

我們改變了 Electron 窗口的既定行為,就會有許多場景下會有問題

問題一:因為阻止了 close 事件,導致 關機 時無法關閉 主窗口,可以使用如下代碼

app.on('before-quit', () => {
    closeMainWindow()
})

在 macOS Linux Windows 下都可以。

問題二:為避免啟動 多個應用

app.on('second-instance', () => {
  const mainWindow = BrowserWindowsMap.get(mainWindowId)
  if (mainWindow) {
    mainWindow.restore()
    mainWindow.show()
  }
})

在 macOS Linux Windows 下都可以

問題三:首次啟動應用程序、嘗試在應用程序已運行時或單擊 應用程序塢站任務欄圖標 時重新激活它

app.on('activate', () => {
  if (mainWindow) {
    mainWindow.restore()
    mainWindow.show()
  }
})

只應用於macOS

問題四: 雙擊托盤圖標 打開APP

tray.on('double-click', () => {
  if (mainWindow) {
    mainWindow.restore()
    mainWindow.show()
  }
})

這樣每個環節的代碼都有,即可實現,具體代碼可參見鏈接

3. 窗口的聚焦和失焦

3.1. 聚焦

3.1.1. 創建窗口時配置

const { BrowserWindow } = require('electron');
const win = new BrowserWindow();
win.loadURL('https://github.com')

focusable:true  窗口便可聚焦,便可以使用聚焦的 API 
focusable:falseWindows 中設置 focusable: false 也意味着設置了skipTaskbar: true. 在 Linux 中設置 focusable: false 時窗口停止與 wm 交互, 並且窗口將始終置頂;

以下討論的情況僅為focusable:true情況下

const { BrowserWindow } = require('electron');
const win = new BrowserWindow() // focusable:true 為默認配置

羅列了一下 API

3.1.2. 關於聚焦的API

API 功能
BrowserWindow.getFocusedWindow() 來獲取聚焦的窗口
win.isFocused() 判斷窗口是否聚焦
win.on('focus',handler) 來監聽窗口是否聚焦
win.focus() 手動聚焦窗口

3.1.3. 其他API副作用和聚焦有關的:

API 功能
win.show() 顯示窗口,並且聚焦於窗口
win.showInactive() 顯示窗口,但是不會聚焦於窗口

3.2. 失焦

3.2.1. 關於失焦的api

API 功能
win.blur() 取消窗口聚焦
win.on('blur',cb) 監聽失焦

3.2.2. 其他api副作用和失焦有關的:

api 功能
win.hide() 隱藏窗口,並且會觸發失焦事件

4. 窗口類型

4.1. 無邊框窗口

4.1.1. 描述

無邊框窗口是不帶外殼(包括窗口邊框、工具欄等),只含有網頁內容的窗口

4.1.2. 實現

Windows macOS Linux

const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600, frame: false })
win.show()

macOS下,還有不同的實現方式,官方文檔

4.1.3. macOS 下獨有的無邊框

  • 配置titleBarStyle: 'hidden'

返回一個隱藏標題欄的全尺寸內容窗口,在左上角仍然有標准的窗口控制按鈕(俗稱“紅綠燈”)

// 創建一個無邊框的窗口
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hidden' })
win.show()

效果如下:
window-type-frame.gif

  • 配置titleBarStyle: 'hiddenInset'

返回一個另一種隱藏了標題欄的窗口,其中控制按鈕到窗口邊框的距離更大。

// 創建一個無邊框的窗口
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
win.show()

效果如下:
window-type-frame2.gif

配置titleBarStyle: 'customButtonsOnHover'

效果如下:
window-type-frame3.gif

4.1.4. 窗口頂部無法拖拽的問題

雖然無邊框窗口,很美觀,可以自定義title;但是改變了Electron窗口頂部的默認行為,就需要使用代碼來兼容它,實現其原來承擔的功能。

window-type1.gif
出現上述情況,是因為在默認情況下, 無邊框窗口是不可拖拽的。 應用程序需要在 CSS 中指定 -webkit-app-region: drag 來告訴 Electron 哪些區域是可拖拽的(如操作系統的標准標題欄),在可拖拽區域內部使用 -webkit-app-region: no-drag 則可以將其中部分區域排除。 請注意, 當前只支持矩形形狀。完整文檔

使用-webkit-app-region: drag 來實現拖拽,但是會導致內部的click事件失效。這個時候可以將需要click元素設置為-webkit-app-region: no-drag。具體的細節 Electron 的issues

為了不影響窗口內的業務代碼,這里拖拽的代碼,應該在preload觸發。

preload 代碼運行,在窗口代碼運行之前

核心代碼:

// 在頂部插入一個可以移動的dom
function initTopDrag() {
  const topDiv = document.createElement('div') // 創建節點
  topDiv.style.position = 'fixed' // 一直在頂部
  topDiv.style.top = '0'
  topDiv.style.left = '0'
  topDiv.style.height = '20px' // 頂部20px才可拖動
  topDiv.style.width = '100%' // 寬度100%
  topDiv.style.zIndex = '9999' // 懸浮於最外層
  topDiv.style.pointerEvents = 'none' // 用於點擊穿透
  // @ts-ignore
  topDiv.style['-webkit-user-select'] = 'none' // 禁止選擇文字
  // @ts-ignore
  topDiv.style['-webkit-app-region'] = 'drag' // 拖動
  document.body.appendChild(topDiv) // 添加節點
}

window.addEventListener('DOMContentLoaded', function onDOMContentLoaded() {
    initTopDrag()
})

在創建窗口時引用 preload 即可

const path = require('path')
const { BrowserWindow } = require('electron')

const BaseWebPreferences = {
  nodeIntegration: true,
  preload: path.resolve(__dirname, './windowType.js'), // 這里引用preload.js 路徑
}

// 主窗口代碼
const win = new BrowserWindow({ webPreferences: BaseWebPreferences, frame: false, titleBarStyle: 'hiddenInset' })
win.loadURL('https://github.com')

便可實現窗口頂部拖拽
window-type.gif
_tips: 如果窗口打開了 __devtools_ ,窗口也是可以拖拽的,只不過這個拖拽體驗不好

4.2. 父子窗口

所謂的父子窗口,就是子窗口永遠在父窗口之上,只要子窗口存在,哪怕位置不在父窗口上方,都是無法操作父窗口

window-type2.gif

const { BrowserWindow } = require('electron')

let top = new BrowserWindow()
let child = new BrowserWindow({ parent: top })
child.show()
top.show()

窗口之間通信 章節中介紹到父子窗口之間的通信;通過 getParentWindow 拿到父窗口的 類BrowserWindowProxy,通過 win.postMessage(message,targetOrigin) 實現通信

4.3. 模態窗口

模態窗口也是一種父子窗口,只不過展示會有不同

const { BrowserWindow } = require('electron')

let top = new BrowserWindow()
let child = new BrowserWindow({ parent: top, modal: true, show: false })
child.loadURL('https://github.com')
child.once('ready-to-show', () => {
  child.show()
})

window-type3.gif

5. 窗口之間的通信

實現窗口通信必須不影響窗口內的業務代碼, jdk 等的注入

5.1. 主進程干預方式

主進程是可以干預渲染進程生成新的窗口的,只需要在創建窗口時,webContents 監聽 new-window

import path from 'path'
import { PRELOAD_FILE } from 'app/config'
import { browserWindow } from 'electron';

const BaseWebPreferences: Electron.BrowserWindowConstructorOptions['webPreferences'] = {
  nodeIntegration: true,
  webSecurity: false,
  preload: path.resolve(__dirname, PRELOAD_FILE),
}


// 創建窗口監聽
browserWindow.webContents.on('new-window', (event, url, frameName, disposition) => {
    event.preventDefault()
    // 在通過BrowserWindow創建窗口
    const win = new BrowserWindow({ 
      show:false, 
      webPreferences: {
        ...BaseWebPreferences,
        additionalArguments:[`--parentWindow=${browserWindow.id}`] // 把父窗口的id傳過去
      } 
    });
    win.loadURl(url);
    win.once('ready-to-show',()=>{
        win.show()
    })
})

preload.js  文件window.process.argv,便能拿到父窗口的id,window.process.argv是一個字符串數組,可以使用yargs來解析

preload.js  代碼

import { argv } from 'yargs'
console.log(argv);

yargv-parse.png
拿到了父窗口的 id ,封裝一下通信代碼,掛載到 window 上

/**
 * 這個是用於窗口通信例子的preload,
 * preload執行順序在窗口js執行順序之前
 */
import { ipcRenderer, remote } from 'electron'
const { argv } = require('yargs')

const { BrowserWindow } = remote

// 父窗口監聽子窗口事件
ipcRenderer.on('communication-to-parent', (event, msg) => {
  alert(msg)
})

const { parentWindowId } = argv
if (parentWindowId !== 'undefined') {
  const parentWindow = BrowserWindow.fromId(parentWindowId as number)
  // 掛載到window
  // @ts-ignore
  window.send = (params: any) => {
    parentWindow.webContents.send('communication-to-parent', params)
  }
}

應用一下試試看:
window-com.gif
這種方法可以實現通信,但是太麻煩了。

5.2. 父子窗口通信

和主進程干預,通過ipc通信方式差不多,只是利用父子窗口這點,不用通過additionalArguments傳遞父窗口id,在子窗口通過window.parent,就可以拿到父窗口

browserWindow.webContents.on('new-window', (event, url, frameName, disposition) => {
    event.preventDefault()
      
    // 在通過BrowserWindow創建窗口
    const win = new BrowserWindow({ 
        show:false, 
        webPreferences:BaseWebPreferences,
        parent:browserWindow // 添加父窗口
      });
    win.loadURl(url);
    win.once('ready-to-show',()=>{
        win.show()
    })
    
})

弊端:子窗口永遠在父窗口之上。

const path = require('path')
const { BrowserWindow } = require('electron')

const BaseWebPreferences = {
  // // 集成node
  nodeIntegration: true,
  // // 禁用同源策略
  // webSecurity: false,
  // 預加載腳本 通過絕對地址注入
  preload: path.resolve(__dirname, './communication2.js'),
}

// 主窗口代碼
const parent = new BrowserWindow({ webPreferences: BaseWebPreferences, left: 100, top: 0 })
parent.loadURL(
  'file:///' + path.resolve(__dirname, '../playground/index.html#/demo/communication-part2/main'),
)
parent.webContents.on('new-window', (event, url, frameName, disposition) => {
  // 阻止默認事件
  event.preventDefault()
  // 在通過BrowserWindow創建窗口
  // 子窗口代碼
  const son = new BrowserWindow({
    webPreferences: BaseWebPreferences,
    parent,
    width: 400,
    height: 400,
    alwaysOnTop: false,
  })
  // son.webContents.openDevTools();
  son.loadURL(
    'file:///' +
      path.resolve(__dirname, '../playground/index.html#/demo/communication-part2/client'),
  )
})

preload.js

import { remote, ipcRenderer } from 'electron'

// 父窗口監聽子窗口事件
ipcRenderer.on('communication-to-parent', (event, msg) => {
  alert(msg)
})

const parentWindow = remote.getCurrentWindow().getParentWindow()
// @ts-ignore
window.sendToParent = (params: any) =>
  parentWindow.webContents.send('communication-to-parent', params)

window-com1.gif
但是必須得是父子窗口,有弊端。

5.3. 使用window.open

終極方法

web 端,使用 window.open  會返回一個 windowObjectReference ,通過這個方法可以實現 postMessage ;但是在 Electron 端,把 window.open 方法重新定義了;使用 window.open 創建一個新窗口時會返回一個 BrowserWindowProxy對象,並提供一個有限功能的子窗口.
MDN文檔 Electron文檔

const  BrowserWindowProxy = window.open('https://github.com', '_blank', 'nodeIntegration=no')
BrowserWindowProxy.postMessage(message, targetOrigin)

代碼精簡,且需要的功能,即符合 BrowserWindow(options) 中 options 配置的,都可以使用 window.open 配置。

6. 全屏、最大化、最小化、恢復

6.1. 全屏

6.1.1. 創建時進入全屏

配置new BrowserWindow({ fullscreen:true })

const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ fullscreen:true,fullscreenable:true })
win.loadURL('https://github.com')

6.1.2. 使用API進入全屏

確保當前窗口的fullscreenable:true,以下API才能使用

  1. win.setFullScreen(flag),設置全屏狀態;
  2. win.setSimpleFullScreen(flag)macOS下獨有,設置簡單全屏。

6.1.3. 全屏狀態的獲取

  1. win.fullScreen,來判斷當前窗口是否全屏;
  2. win.isFullScreen()macOS獨有;
  3. win.isSimpleFullScreen()macOS獨有。

6.1.4. 全屏事件的監聽

  1. rezise 調整窗口大小后觸發;
  2. enter-full-screen 窗口進入全屏狀態時觸發;
  3. leave-full-screen 窗口離開全屏狀態時觸發;
  4. enter-html-full-screen 窗口進入由HTML API 觸發的全屏狀態時觸發;
  5. leave-html-full-screen 窗口離開由HTML API觸發的全屏狀態時觸發。

6.1.5. HTML API無法和窗口聯動問題

const path = require('path')
const { BrowserWindow } = require('electron')
const BaseWebPreferences = { 
    nodeIntegration: true,
    preload: path.resolve(__dirname, './fullScreen.js'), 
};
const win = new BrowserWindow({ webPreferences: BaseWebPreferences })
win.loadURL('file:///' + path.resolve(__dirname, '../playground/index.html#/demo/full-screen'))

使用按鈕全屏和退出全屏是可以的,但是先點擊左上角🚥全屏,再使用按鈕退出全屏,是不行的。因為無法知道當前的狀態是全屏,還是不是全屏。

解決辦法:,將win.setFullScreen(flag)方法掛載到窗口的window
加載這樣一段preload.js代碼即可

import { remote } from 'electron'

const setFullScreen = remote.getCurrentWindow().setFullScreen
const isFullScreen = remote.getCurrentWindow().isFullScreen

window.setFullScreen = setFullScreen
window.isFullScreen = isFullScreen

_ setFullScreen文檔 https://www.electronjs.org/docs/api/browser-window#winsetfullscreenflag isFullScreen 文檔_https://www.electronjs.org/docs/api/browser-window#winisfullscreen

6.2. 最大化、最小化

6.2.1. 創建窗口配置

完整API文檔

const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ minWidth:300,minHeight:300,maxWidth:500,maxHeight:500,width:600,height:600 })
win.loadURL('https://github.com')

當使用 minWidth/maxWidth/minHeight/maxHeight 設置最小或最大窗口大小時, 它只限制用戶。 它不會阻止您將不符合大小限制的值傳遞給 setBounds/setSizeBrowserWindow 的構造函數。

6.2.2. 相關事件

事件名稱 觸發條件
maximize 窗口最大化時觸發
unmaximize 當窗口從最大化狀態退出時觸發
minimize 窗口最小化時觸發
restore 當窗口從最小化狀態恢復時觸發

6.2.3. 相關狀態API

  1. win.minimizable 窗口是否可以最小化
  2. win.maximizable 窗口是否可以最大化
  3. win.isMaximized() 是否最大化
  4. win.isMinimized() 是否最小化

6.2.4. 控制API

  1. win.maximize() 使窗口最大化
  2. win.unmaximize() 退出最大化
  3. win.minimize() 使窗口最小化
  4. win.unminimize() 退出最小化

6.3. 窗口恢復

win.restore() 將窗口從最小化狀態恢復到以前的狀態。在前面的例子 主窗口隱藏和恢復也有用到這個api

7. 窗口各事件觸發順序

window-event.png

7.1. 窗口加載時

BrowserWindow實例:即 new BrowserWindow() 返回的實例對象
webContents: 即 BrowserWindow 實例中的 webContents 對象
webPreferences: 即 new BrowserWindow(options) 中 options 的 webPreferences 配置對象

從上到下,依次執行

環境 事件 觸發時機
webPreferences的preload - 在頁面運行其他腳本之前預先加載指定的腳本 無論頁面是否集成Node, 此腳本都可以訪問所有Node API 腳本路徑為文件的絕對路徑。
webContents did-start-loading 當tab中的旋轉指針(spinner)開始旋轉時,就會觸發該事件
webContents did-start-navigation 當窗口開始導航是,觸發該事件
窗口中的JavaScript DOMContentLoaded 初始的 HTML 文檔被完全加載和解析完成
窗口中的JavaScript load 頁面資源全部加載完成之時
BrowserWindow實例 show 窗口顯示時觸發時
webContents did-frame-navigate frame導航結束時時
webContents did-navigate main frame導航結束時時
BrowserWindow實例 page-title-updated 文檔更改標題時觸發
webContents page-title-updated 文檔更改標題時觸發
webContents dom-ready 一個框架中的文本加載完成后觸發該事件
webContents did-frame-finish-load 當框架完成導航(navigation)時觸發
webContents did-finish-load 導航完成時觸發,即選項卡的旋轉器將停止旋轉
webContents did-stop-loading 當tab中的旋轉指針(spinner)結束旋轉時,就會觸發該事件

7.2. 窗口加載完畢,用戶觸發事件(不包括resize和move)

事件 作用
page-title-updated 文檔更改標題時觸發
blur 當窗口失去焦點時觸發
focus 當窗口獲得焦點時觸發
hide 窗口隱藏
show 窗口顯示
maximize 窗口最大化時觸發(mac是雙擊title)
unmaximize 當窗口從最大化狀態退出時觸發
enter-full-screen 窗口進入全屏狀態時觸發
leave-full-screen 窗口離開全屏狀態時觸發
enter-html-full-screen 窗口進入由HTML API 觸發的全屏狀態時觸發
leave-html-full-screen 窗口離開由HTML API觸發的全屏狀態時觸發
always-on-top-changed 設置或取消設置窗口總是在其他窗口的頂部顯示時觸發。
app-command window linux獨有

7.3. 用戶移動窗口

  1. 移動窗口之前 will-move
  2. 移動窗口中 move
  3. 移動之后 moved

7.4. 用戶改變窗口大小

  1. 改變之前 will-resize
  2. 改變之后 resize

7.5. 窗口的內容異常事件(webContent事件)

事件名 錯誤類型
unresponsive 網頁變得未響應時觸發
responsive 未響應的頁面變成響應時觸發
did-fail-load 加載失敗,錯誤碼
did-fail-provisional-load 頁面加載過程中,執行了window.stop()
did-frame-finish-load
crashed 渲染進程崩潰或被結束時觸發
render-process-gone 渲染進程意外失敗時發出
plugin-crashed 有插件進程崩潰時觸發
certificate-error 證書的鏈接驗證失敗
preload-error preload.js拋出錯誤

7.6. 窗口關閉(包括意外關閉)

  • 關閉之前:觸發主進程中注冊的 close  事件
  • 窗口內的 JavaScript  執行 window.onbeforeunload
  • 窗口內的 JavaScript  執行 window.onunload
  • 關閉之后:觸發主進程中注冊的 closed  事件

對 Electron 感興趣?請關注我們的開源項目 Electron Playground,帶你極速上手 Electron。

我們每周五會精選一些有意思的文章和消息和大家分享,來掘金關注我們的 曉前端周刊


我們是好未來 · 曉黑板前端技術團隊。
我們會經常與大家分享最新最酷的行業技術知識。
歡迎來 知乎掘金SegmentfaultCSDN簡書開源中國博客園 關注我們。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM