窗口管理
主进程目录
/src/main 我把主进程稍微做了一些拆分,方便管理,亦可自行拆分,怎么喜欢怎么来。
- index.d.ts 一些模块类型声明忽略文件
- index.ts 主进程
- myWindow.ts 窗口管理
- operation.ts 功能函数
- 只适配了windows,mac和linux没有做适配
窗口管理
单纯做个简单壁纸软件,没啥私密信息,webSecurity: false, allowRunningInsecureContent: true,等一些可能存在安全隐患的配置我都打开了,可自行查看官方配置,自行斟酌是否需要打开。
// myWindow.ts 窗口管理
import { app, shell, BrowserWindow, nativeImage } from 'electron'
import { join } from 'path'
import { is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import appIcon from '../../build/icon.png?asset'
// 子窗口
export default () => {
// 窗口数组,管理所有窗口
let childWindows: any[] = []
// 主窗口
function mainWindow(): void {
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
autoHideMenuBar: true,
title: '壁纸集',
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
// 预加载脚本
preload: join(__dirname, '../preload/index.js'),
nodeIntegration: true,
contextIsolation: false,
webSecurity: false, // 关闭安全策略 ,允许跨域加载
allowRunningInsecureContent: true, // 允许加载本地资源(http协议)
sandbox: false
},
icon: nativeImage.createFromPath(appIcon)
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// 加载页面
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
// 窗口关闭事件
mainWindow.on('close', () => {
// 主窗口关闭时,关闭所有窗口
childWindows = childWindows.filter(childWindow => !childWindow.isDestroyed())
childWindows.forEach(childWindow => childWindow.close())
app.quit()
})
}
const childWindow = (param: any) => {
const exits = childWindows.find(childWindow => childWindow["name"] === param.name && !childWindow.isDestroyed())
// 判断该窗口是否存在,如果存在则关闭
// if (exits) {
// exits.close()
// childWindows = childWindows.filter(childWindow => childWindow["name"] !== name)
// }
// 如果窗口存在则实现窗口最小化的切换
if (exits) {
if (exits.isMinimized()) {
exits.restore()
} else {
exits.minimize()
}
return
}
const View = new BrowserWindow({
width: param.width ? param.width : 850,
height: param.height ? param.height : 610,
show: true,
// 隐藏默认菜单
autoHideMenuBar: true,
// 设置标题
title: param.title,
// 窗口置顶
alwaysOnTop: false,
webPreferences: {
// 预加载脚本
preload: join(__dirname, '../preload/index.js'),
nodeIntegration: true,
contextIsolation: false,
webSecurity: false
},
icon: nativeImage.createFromPath(appIcon)
})
// 给窗口设置 name (唯一标识,可自行设置)
View["name"] = param.name
// 加载页面
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
View.loadURL(`http://localhost:5173/#${param.url}`)
} else {
View.loadFile(join(__dirname, '../renderer/index.html'), {
hash: param.url
})
}
childWindows.push(View)
}
return {
mainWindow,
childWindow
}
}
主要配置
判断是开发环境还是生产环境,param.url("/home")是正常的路由路径
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
// 开发环境打开这个地址 http://localhost:5173 根据你自己配置的启动端口更改,我用的是默认
View.loadURL(`http://localhost:5173/#${param.url}`)
} else {
// 生产环境使用该路径
View.loadFile(join(__dirname, '../renderer/index.html'), {
hash: param.url
})
}
何时使用,如何使用?
- 主窗口不用多说,启动软件的时候执行。
- 子窗口根据你的需求自定义,例如:点击图片查看详情是否需要打开新窗口,不需要则直接跳转路由即可,如果需要打开子窗口,则需要通知主进程,主进程在调用子窗口。
- 通知主进程必然离不开通信,可自行前往electron官网查看通信方式,这里只举例一种通信方式。
进程通信和使用
ipcMain.handle()方法用于注册一个消息处理函数,用来处理特定的 IPC 消息ipcRenderer.invoke方法向主进程发送 IPC 消息
主进程 注册消息( src/main/index.ts )
// 主进程中 src/main/index.ts
app.whenReady().then(() => {
······
ipcMain.handle('new-open', (_, param) => {
// 打开新窗口
childWindow(param)
})
})
预加载脚本 向主进程发送IPC消息( src/preload/index.ts )
// 预加载脚本 src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
const api = {
···,
newOpen: (params: any) => {
return ipcRenderer.invoke('new-open', params)
}
}
// 是否采用上下文隔离
if (process.contextIsolated) {
// 是
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
// 否
// @ts-ignore (define in dts)
window.electron = electronAPI
// @ts-ignore (define in dts)
window.api = api
}
渲染进程 执行脚本 打开新窗口 ( src/renderer/src/views/Home/index.vue )
import { onMounted} from 'vue'
let myApi
// 点击事件
const goMoHu = () => {
// 窗口定义的参数,可自行定义 ,见上方 窗口管理 中的子窗口相关配置
const obj = {
url: '/mohu',
name: 'mohuWindow',
title: '萌虎壁纸'
}
myApi.newOpen(obj)
}
onMounted(()=>{
// 接收api,当然也可以直接调用
myApi = window.api
})
至此,你就完成了子窗口的创建,传入不同的路由参数路径打开不同的子窗口。