2、路由窗口管理

128 阅读3分钟

窗口管理

主进程目录

/src/main 我把主进程稍微做了一些拆分,方便管理,亦可自行拆分,怎么喜欢怎么来。

  1. index.d.ts 一些模块类型声明忽略文件
  2. index.ts 主进程
  3. myWindow.ts 窗口管理
  4. operation.ts 功能函数
  5. 只适配了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
})

至此,你就完成了子窗口的创建,传入不同的路由参数路径打开不同的子窗口。