主頁 > 企業開發 > 【Electron Playground 系列】視窗篇

【Electron Playground 系列】視窗篇

2020-12-15 13:15:49 企業開發

作者: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,

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


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

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/235028.html

標籤:其他

上一篇:Taro 3.1 beta 發布: 開放式架構新增 4 端支持

下一篇:最近的總結:markdown、hr、em、后代選擇器

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more