基于electron25+vite4创建多窗口|vue3+electron25新开模态窗体 - xiaoyan2017
source link: https://www.cnblogs.com/xiaoyan2017/p/17442502.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
基于electron25+vite4创建多窗口|vue3+electron25新开模态窗体
在写这篇文章的时候,查看了下electron最新稳定版本由几天前24.4.0升级到了25了,不得不说electron团队迭代速度之快!
前几天有分享一篇electron24整合vite4全家桶技术构建桌面端vue3应用示例程序。
https://www.cnblogs.com/xiaoyan2017/p/17436076.html
这次继续接着上次项目,主要介绍electron25结合vue3技术实现创建多开窗口及窗口间主/渲染进程通信知识。
随着electron快速更新,结合vite的高效构建运行速度,现在新开一个独立窗口,打开速度极快。
electron官网主进程模块BrowserWindow用于创建一个新窗口的方法,提供了非常丰富的API操作用法。
https://www.electronjs.org/docs/latest/api/browser-window
// In the main process. const { BrowserWindow } = require('electron') const win = new BrowserWindow({ width: 800, height: 600 }) // Load a remote URL win.loadURL('https://github.com') // Or load a local HTML file win.loadFile('index.html')
如果每次都new一个BrowserWindow窗口,显得有些笨拙且复杂。今天要分享的是封装BrowserWindow方法,只需传入配置参数,即可快速生成一个独立窗口。
createWin({ title: '关于About.vue', route: '/about', width: 600, height: 400, background: '#fafffa', resize: true })
新建一个windows/index.js文件。
/** * 封装多窗口管理器 * @author YXY */ const { app, BrowserWindow, ipcMain } = require('electron') const { join } = require('path') process.env.ROOT = join(__dirname, '../../') const isDevelopment = process.env.NODE_ENV == 'development' // const winURL = isDevelopment ? 'http://localhost:3000/' : join(__dirname, 'dist/index.html') const winURL = isDevelopment ? process.env.VITE_DEV_SERVER_URL : join(process.env.ROOT, 'dist/index.html') // 配置参数 const defaultConfig = { id: null, // 窗口唯一id background: '#fff', // 背景色 route: '', // 路由地址url title: '', // 标题 data: null, // 传入数据参数 width: '', // 窗口宽度 height: '', // 窗口高度 minWidth: '', // 窗口最小宽度 minHeight: '', // 窗口最小高度 x: '', // 窗口相对于屏幕左侧坐标 y: '', // 窗口相对于屏幕顶端坐标 resize: true, // 是否支持缩放 maximize: false, // 最大化窗口 isMultiWin: false, // 是否支持多开窗口 isMainWin: false, // 是否主窗口 parent: '', // 父窗口(需传入父窗口id) modal: false, // 模态窗口(模态窗口是浮于父窗口上,禁用父窗口) alwaysOnTop: false // 置顶窗口 } class MultiWindows { constructor() { // 主窗口 this.mainWin = null // 窗口组 this.winLs = {} // ... } winOpts() { return { // 窗口图标 icon: join(process.env.ROOT, 'resource/shortcut.ico'), backgroundColor: '#fff', autoHideMenuBar: true, titleBarStyle: 'hidden', width: 1000, height: 640, resizable: true, minimizable: true, maximizable: true, frame: false, show: false, webPreferences: { contextIsolation: true, // 启用上下文隔离(为了安全性)(默认true) // nodeIntegration: false, // 启用Node集成(默认false) preload: join(process.env.ROOT, 'resource/preload.js'), // devTools: true, // webSecurity: false } } } // 创建新窗口 createWin(options) { const args = Object.assign({}, defaultConfig, options) console.log(args) // 判断窗口是否存在 for(let i in this.winLs) { if(this.getWin(i) && this.winLs[i].route === args.route && !this.winLs[i].isMultiWin) { this.getWin(i).focus() return } } let opt = this.winOpts() if(args.parent) { opt.parent = this.getWin(args.parent) } if(typeof args.modal === 'boolean') opt.modal = args.modal if(typeof args.resize === 'boolean') opt.resizable = args.resize if(typeof args.alwaysOnTop === 'boolean') opt.alwaysOnTop = args.alwaysOnTop if(args.background) opt.backgroundColor = args.background if(args.width) opt.width = args.width if(args.height) opt.height = args.height if(args.minWidth) opt.minWidth = args.minWidth if(args.minHeight) opt.minHeight = args.minHeight if(args.x) opt.x = args.x if(args.y) opt.y = args.y console.log(opt) // 创建窗口对象 let win = new BrowserWindow(opt) // 是否最大化 if(args.maximize && args.resize) { win.maximize() } this.winLs[win.id] = { route: args.route, isMultiWin: args.isMultiWin } args.id = win.id // 加载页面 let $url if(!args.route) { if(process.env.VITE_DEV_SERVER_URL) { // 打开开发者调试工具 // win.webContents.openDevTools() $url = process.env.VITE_DEV_SERVER_URL }else { $url = winURL } }else { $url = `${winURL}#${args.route}` } win.loadURL($url) /*if(process.env.VITE_DEV_SERVER_URL) { win.loadURL($url) }else { win.loadFile($url) }*/ win.webContents.openDevTools() win.once('ready-to-show', () => { win.show() }) win.on('close', () => win.setOpacity(0)) // 初始化渲染进程 win.webContents.on('did-finish-load', () => { // win.webContents.send('win-loaded', '加载完成~!') win.webContents.send('win-loaded', args) }) } // 获取窗口 getWin(id) { return BrowserWindow.fromId(Number(id)) } // 获取全部窗口 getAllWin() { return BrowserWindow.getAllWindows() } // 关闭全部窗口 closeAllWin() { try { for(let i in this.winLs) { if(this.getWin(i)) { this.getWin(i).close() }else { app.quit() } } } catch (error) { console.log(error) } } // 开启主进程监听 ipcMainListen() { // 设置标题 ipcMain.on('set-title', (e, data) => { const webContents = e.sender const wins = BrowserWindow.fromWebContents(webContents) wins.setTitle(data) // const wins = BrowserWindow.getFocusedWindow() // wins.setTitle('啦啦啦') }) // 是否最大化(方法一) /*ipcMain.on('isMaximized', e => { const win = BrowserWindow.getFocusedWindow() e.sender.send('mainReplay', win.isMaximized()) })*/ // 是否最大化(方法二) ipcMain.handle('isMaximized', (e) => { const win = BrowserWindow.getFocusedWindow() return win.isMaximized() }) ipcMain.on('min', e => { const win = BrowserWindow.getFocusedWindow() win.minimize() }) ipcMain.handle('max2min', e => { const win = BrowserWindow.getFocusedWindow() if(win.isMaximized()) { win.unmaximize() return false }else { win.maximize() return true } }) ipcMain.on('close', (e, data) => { // const wins = BrowserWindow.getFocusedWindow() // wins.close() this.closeAllWin() }) // ... } } module.exports = MultiWindows
在主进程入口background.js文件引入封装窗口。
const { app, BrowserWindow, ipcMain } = require('electron') const { join } = require('path') const MultiWindows = require('./src/windows') // 屏蔽安全警告 // ectron Security Warning (Insecure Content-Security-Policy) process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' const createWindow = () => { let window = new MultiWindows() window.createWin({isMainWin: true}) window.ipcMainListen() } app.whenReady().then(() => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })
在主进程中做一个ipcMain监听,用来创建独立窗口。
ipcMain.on('win-create', (event, args) => this.createWin(args))
新建windows/action.js文件,处理渲染器进程到主进程的异步通信,可以发送同步或异步的消息到主进程,也可以接收主进程发送的消息。
/** * 创建新窗口 * @param {object} args | {width: 640, height: 480, route: '/home'} */ export function createWin(args) { window.electronAPI.send('win-create', args) } /** * 设置窗口 * @param {string} type | 'show'/'hide'/'close'/'min'/'max'/'max2min'/'restore'/'reload' * @param {number} id */ export function setWin(type, id) { window.electronAPI.send('win-' + type, id) } /** * 创建登录窗口 */ export function loginWin() { createWin({ isMainWin: true, title: '登录', route: '/login', width: 550, height: 320, resize: false, alwaysOnTop: true, }) }
在vue页面中调用上面封装的方法。
<template> <div class="home"> ... <Button type="success" @click="openWin">打开Manage窗口(设置parent)</Button> <Button type="success" @click="openWin1">打开Me窗口(设置resizable/isMultiWin)</Button> <Button type="success" @click="openWin2">打开User窗口</Button> </div> </template> <script> import { winCfg, createWin } from '@/windows/action' export default { name: 'Home', setup() { const openWin = () => { MessageBox.confirm('提示', '确定打开Manage页面吗? 【设置parent属性】', { callback: action => { if(action == 'confirm') { createWin({ title: 'Manage.vue', route: '/manage', width: 600, height: 400, background: '#09f', parent: winCfg.window.id, // modal: true }) }else if(action == 'cancel') { Message.info('您已取消!') } } }) } const openWin1 = () => { // 左上角 // let posX = 0 // let posY = 0 // 右下角 let posX = window.screen.availWidth - 850 let posY = window.screen.availHeight - 600 MessageBox.confirm('提示', '确定打开Me页面吗?', { callback: action => { if(action == 'confirm') { createWin({ title: 'Me.vue', route: '/me?name=Andy', width: 850, height: 600, x: posX, y: posY, background: 'yellow', resize: false, isMultiWin: true, maximize: true }) }else if(action == 'cancel') { Message.info('您已取消!') } } }) } const openWin2 = () => { MessageBox.confirm('提示', '确定打开User页面吗?', { callback: action => { if(action == 'confirm') { createWin({ title: 'User.vue', route: '/user', width: 700, height: 550, minWidth: 300, minHeight: 300, data: { name: 'Andy', age: 20 }, background: 'green', isMultiWin: true }) }else if(action == 'cancel') { Message.info('您已取消!') } } }) } // ... return { openWin, openWin1, openWin2, // ... } } } </script>
设置 frame: false 创建无边框窗口。
设置 -webkit-app-region: drag 来实现自定义拖拽区域。设置后的按钮操作无法响应其它事件,只需设置 -webkit-app-region: no-drag 即可实现响应事件。
electron+vite提供的一些环境变量。
process.env.NODE_ENV process.env.VITE_DEV_SERVER_URL
在开发环境,加载vite url,生产环境,则加载vite build出来的html。
Ok,综上就是electron25+vite4结合构建跨端应用的一些分享,希望对大家有所帮助哈~~
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK