关注公众号: 微信搜索 前端工具人
; 收货更多的干货
原文链接: 自己掘金文章: 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
方法
有不对之处欢迎指正, 代码有删减,没做测试;
只是作为分享,让有需要的少走弯路,节省百度时间