主頁 > 前端設計 > 【Electron Playground 系列】視窗篇

【Electron Playground 系列】視窗篇

2020-12-15 13:11:12 前端設計

作者: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/qianduan/235021.html

標籤:HTML5

上一篇:Vue函式有回呼引數,添加引數時如何保留默認回呼引數

下一篇:求解答

標籤雲
其他(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)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

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

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more