關注公眾號: 微信搜索 前端工具人
; 收貨更多的干貨
原文鏈接: 自己掘金文章: https://juejin.cn/post/7067815153374330888/
一、需求
主要是一下幾個常見的需求:
- 自定義頂部菜單欄, 可拖拽;
- 自定義最小化、最大化、退出按鈕、刷新按鈕(類似瀏覽器的重新加載、用於開發階段調試);
- 小窗口 - 中窗口 - 全屏窗口, 相互切換;
二、electron 解讀
electron
區分了兩種進程:主進程和渲染進程
2.1 主進程:
- 創建進程、窗口...
- 控制應用生命周期(啟動、退出APP、事件監聽..)
- 調用系統底層功能 (
Electron API
)、調用原生API
(Node.js
與本地交互...)
2.2 渲染進程
- 主要是內置
Chromium
瀏覽, 來實現頁面的渲染; - 可以理解成
electron
渲染進程 為Chromium
的窗口; 所以和日常開發區別不大
三、需求實現
項目入口文件 render.js
, 主進程事件文件 ipc.event.js
render.js
'use strict'
import { app, protocol, BrowserWindow, Menu } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import initIpcEvent from './services/ipc.event'
const path = require('path');
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow () {
// Create the browser window.
const win = new BrowserWindow({
width: 400,
height: 500,
center: true,
frame: false,
useContentSize: true,
// resizable: false,
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
},
icon: path.join(__dirname, '../public/favicon32.ico')
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
// win.webContents.openDevTools()
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
win.setMenu(null)
global.mainWindow = win
// 初始化進程之間事件監聽
initIpcEvent()
// 隱藏菜單
createMenu()
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
// await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
// 設置菜單欄
function createMenu() {
// darwin表示macOS,針對macOS的設置
if (process.platform === 'darwin') {
const template = [{
label: 'Electron',
submenu: [{
role: 'about'
}, {
role: 'quit'
}]
}]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
} else {
// windows及linux系統
Menu.setApplicationMenu(null)
}
}
ipc.event.js
import { ipcMain, app, BrowserWindow } from 'electron'
export default function () {
ipcMain.on('toggle-mini', (event, params) => {
if (params.value) {
global.mainWindow.hide()
} else {
global.mainWindow.show()
}
})
ipcMain.on('window-min', () => {
global.mainWindow.minimize()
global.mainWindow.setResizable(true)
})
ipcMain.on('window-login', () => {
global.mainWindow.setMinimumSize(400, 500)
global.mainWindow.center()
global.mainWindow.setResizable(false)
})
ipcMain.on('window-password', () => {
global.mainWindow.setSize(1366, 768)
global.mainWindow.center()
global.mainWindow.setResizable(true)
})
ipcMain.on('window-max', () => {
if (global.mainWindow.isMaximized()) {
global.mainWindow.restore()
} else {
global.mainWindow.maximize()
}
// global.mainWindow.setMinimumSize(1600, 900)
global.mainWindow.setMinimumSize(1200, 800)
global.mainWindow.center()
})
ipcMain.on('window-hide', () => {
global.mainWindow.hide()
})
ipcMain.on('window-show', () => {
global.mainWindow.show()
})
ipcMain.on('window-refresh', () => {
global.mainWindow.reload();
})
// 關閉當前窗口
ipcMain.on('window-close', () => {
console.log("window-close")
global.mainWindow.close()
})
// 關閉所有窗口
ipcMain.on('window-all-close', () => {
console.log("window-all-close")
const wins = BrowserWindow.getAllWindows()
for (let i = 0; i < wins.length; i++) {
wins[i].close()
}
})
// 所有窗口都將立即被關閉,而不詢問用戶,而且 before-quit 和 will-quit 事件也不會被觸發。
ipcMain.on('app-exit', () => {
app.exit()
})
ipcMain.on('quit-and-open', (event, data) => {
global.downloadFile = data
app.quit()
})
}
3.1 自定義頂部導航欄
首先要隱藏調自帶的頂部導航欄
// createMenu() 方法就是隱藏頂部導航欄
/ 拖拽,樣式 -webkit-app-region: drag;
3.2 自定義最小化、最大化、退出按鈕、刷新按鈕;
<!-- 頂部導航欄 -->
<template>
...
<header class="common-header">
<i class="el-icon-refresh-right" title="刷新" @click="onChangeWindow('refresh')"></i>
<i class="el-icon-switch-button" title="退出登錄" @click="onChangeWindow('logout')"></i>
<i class="el-icon-minus" title="最小化" @click="onChangeWindow('min')"></i>
<i v-show="isMax" class="el-icon-copy-document" title="還原" @click="onChangeWindow('scale')"></i>
<i v-show="!isMax" class="max-window" title="最大化" @click="onChangeWindow('scale')">
<span></span>
</i>
<i class="el-icon-close" title="關閉" @click="onChangeWindow('close')"></i>
</header>
...
</template>
<script>
import { ipcRenderer, remote } from 'electron'
...
// 窗口切換
onChangeWindow (type) {
switch (type) {
case 'min':
ipcRenderer.send('window-min')
break;
case 'scale':
ipcRenderer.send('window-max')
const winInfo = remote.getCurrentWindow()
this.isMax = winInfo.isMaximized()
break;
case 'close':
ipcRenderer.send('window-min')
break;
case 'logout':
ipcRenderer.send('window-min')
setTimeout(() => {
this.$router.push('/')
ipcRenderer.send('window-login')
ipcRenderer.send('window-show')
}, 150)
break;
case 'refresh':
ipcRenderer.send('window-refresh')
break;
}
...
</script>
3.3 小窗口 - 中窗口 - 全屏窗口, 相互切換;
- 初始化登錄界面是小窗口類似於微信登錄一樣
400 * 500
; - 登錄后跳轉到程序主界面,
全屏
; - 忘記密碼界面(中窗口)1366 * 788
自己開發時,在全屏狀態下切換回小窗口切換不了,百度了蠻久,也沒結果;
后面發現改變全屏狀態后就能切換...
方法:先最小化窗口
、在 setMinimumSize
改變尺寸, 在顯示
, 相當於加了個過渡一下,變換過程也沒那么死板
具體看3.2
的 onChangeWindow
里的 logout
方法
有不對之處歡迎指正, 代碼有刪減,沒做測試;
只是作為分享,讓有需要的少走彎路,節省百度時間