【Electron Playground 系列】視窗篇

曉前端發表於2020-12-15

作者: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簡書開源中國部落格園 關注我們。

相關文章