今天給大家分享一些Vue3整合Electron新建多開視窗的相關知識。
至於如何搭建vue3+electron專案,大家可以去看看之前的這篇分享文章。
一般建立多開視窗思路都是需要新建就會建立一個new BrowserWindow({...}),一個兩個還行,但是當需要建立多個窗體,比如QQ,這樣操作就顯得太low。
這時候如果能封裝成一個函式,每一次新建就傳入相應引數,這樣就簡便多了~~ 類似如下程式碼片段。
windowCreate({ title: '使用者中心', route: '/ucenter', width: 750, height: 500, backgroundColor: '#f9f9f9', resizable: false, maximize: true, modal: true,})
vue3+electron整合的專案,根目錄會有一個background.js主入口檔案。
'use strict'import { app, BrowserWindow } from 'electron'const isDevelopment = process.env.NODE_ENV !== 'production'import { Window } from './windows'async function createWindow() { let window = new Window() window.listen() window.createWindows({isMainWin: true}) window.createTray()}app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() }})app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow()})app.on('ready', async () => { createWindow()})if (isDevelopment) { if (process.platform === 'win32') { process.on('message', (data) => { if (data === 'graceful-exit') { app.quit() } }) } else { process.on('SIGTERM', () => { app.quit() }) }}
然後新建一個windows.js,用來處理主程序相關函式。
import { app, BrowserWindow, ipcMain, Menu, Tray } from "electron";import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'import path from 'path'export const windowsCfg = { id: '', //唯一id title: '', //視窗標題 width: '', //寬度 height: '', //高度 minWidth: '', //最小寬度 minHeight: '', //最小高度 route: '', // 頁面路由URL resizable: true, //是否支援調整視窗大小 maximize: false, //是否最大化 backgroundColor:'#eee', //視窗背景色 data: null, //資料 isMultiWindow: false, //是否支援多開視窗 isMainWin: false, //是否主視窗 parentId: '', //父視窗id 建立父子視窗 modal: false, //模態視窗}/** * 視窗配置 */export class Window { constructor() { this.main = null; //當前頁 this.group = {}; //視窗組 this.tray = null; //托盤 } // 視窗配置 winOpts(wh=[]) { return { width: wh[0], height: wh[1], backgroundColor: '#f00', autoHideMenuBar: true, titleBarStyle: "hidden", resizable: true, minimizable: true, maximizable: true, frame: false, show: false, webPreferences: { contextIsolation: false, //上下文隔離 // nodeIntegration: true, //啟用Node整合(是否完整的支援 node) nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, // devTools: false, webSecurity: false, enableRemoteModule: true, //是否啟用遠端模組(即在渲染程序頁面使用remote) } } } // 獲取視窗 getWindow(id) { return BrowserWindow.fromId(id) } // 獲取全部視窗 getAllWindows() { return BrowserWindow.getAllWindows() } // 建立視窗 createWindows(options) { console.log('------------開始建立視窗...') console.log(options) let args = Object.assign({}, windowsCfg, options) console.log(args) // 判斷視窗是否存在 for(let i in this.group) { if(this.getWindow(Number(i)) && this.group[i].route === args.route && !this.group[i].isMultiWindow) { this.getWindow(Number(i)).focus() return } } let opt = this.winOpts([args.width || 800, args.height || 600]) if(args.parentId) { console.log('parentId:' + args.parentId) opt.parent = this.getWindow(args.parentId) } else if(this.main) { console.log(666) } if(typeof args.modal === 'boolean') opt.modal = args.modal if(typeof args.resizable === 'boolean') opt.resizable = args.resizable if(args.backgroundColor) opt.backgroundColor = args.backgroundColor if(args.minWidth) opt.minWidth = args.minWidth if(args.minHeight) opt.minHeight = args.minHeight console.log(opt) let win = new BrowserWindow(opt) console.log('視窗id:' + win.id) this.group[win.id] = { route: args.route, isMultiWindow: args.isMultiWindow, } // 是否最大化 if(args.maximize && args.resizable) { win.maximize() } // 是否主視窗 if(args.isMainWin) { if(this.main) { console.log('主視窗存在') delete this.group[this.main.id] this.main.close() } this.main = win } args.id = win.id win.on('close', () => win.setOpacity(0)) // 開啟網址(載入頁面) /** * 開發環境: http://localhost:8080 * 正式環境: app://./index.html */ let winURL if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode // win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) winURL = args.route ? `http://localhost:8080${args.route}` : `http://localhost:8080` // 開啟開發者除錯工具 // if (!process.env.IS_TEST) win.webContents.openDevTools() } else { createProtocol('app') // Load the index.html when not in development // win.loadURL('app://./index.html') winURL = args.route ? `app://./index.html${args.route}` : `app://./index.html` } win.loadURL(winURL) win.once('ready-to-show', () => { win.show() }) // 遮蔽視窗選單(-webkit-app-region: drag) win.hookWindowMessage(278, function(e){ win.setEnabled(false) setTimeout(() => { win.setEnabled(true) }, 100) return true }) } // 關閉所有視窗 closeAllWindow() { for(let i in this.group) { if(this.group[i]) { if(this.getWindow(Number(i))) { this.getWindow(Number(i)).close() } else { console.log('---------------------------') app.quit() } } } } // 建立托盤 createTray() { console.log('建立托盤') const contextMenu = Menu.buildFromTemplate([ { label: '顯示', click: () => { for(let i in this.group) { if(this.group[i]) { // this.getWindow(Number(i)).show() let win = this.getWindow(Number(i)) if(!win) return if(win.isMinimized()) win.restore() win.show() } } } }, { label: '退出', click: () => { app.quit() } } ]) console.log(__dirname) const trayIco = path.join(__dirname, '../static/logo.png') console.log(trayIco) this.tray = new Tray(trayIco) this.tray.setContextMenu(contextMenu) this.tray.setToolTip(app.name) } // 開啟監聽 listen() { // 關閉 ipcMain.on('window-closed', (event, winId) => { if(winId) { this.getWindow(Number(winId)).close() if(this.group[Number(winId)]) delete this.group[Number(winId)] } else { this.closeAllWindow() } }) // 隱藏 ipcMain.on('window-hide', (event, winId) => { if(winId) { this.getWindow(Number(winId)).hide() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).hide() } }) // 顯示 ipcMain.on('window-show', (event, winId) => { if(winId) { this.getWindow(Number(winId)).show() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).show() } }) // 最小化 ipcMain.on('window-mini', (event, winId) => { if(winId) { this.getWindow(Number(winId)).minimize() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).minimize() } }) // 最大化 ipcMain.on('window-max', (event, winId) => { if(winId) { this.getWindow(Number(winId)).maximize() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).maximize() } }) // 最大化/最小化 ipcMain.on('window-max-min-size', (event, winId) => { if(winId) { if(this.getWindow(winId).isMaximized()) { this.getWindow(winId).unmaximize() }else { this.getWindow(winId).maximize() } } }) // 還原 ipcMain.on('window-restore', (event, winId) => { if(winId) { this.getWindow(Number(winId)).restore() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).restore() } }) // 重新載入 ipcMain.on('window-reload', (event, winId) => { if(winId) { this.getWindow(Number(winId)).reload() } else { for(let i in this.group) if(this.group[i]) this.getWindow(Number(i)).reload() } }) // 建立視窗 ipcMain.on('window-new', (event, args) => this.createWindows(args)) }}
新建plugin.js,用來封裝一些呼叫方法。
/** * 新建視窗 */export function windowCreate(args) { console.log(args) ipcRenderer.send('window-new', args)}/** * 關閉視窗 */export function windowClose(id) { console.log('視窗id:' + id) ipcRenderer.send('window-closed', id)}// ...
在渲染程序.vue頁面,呼叫plugin.js裡面的函式。
<template> <div class="app"> <img src="@assets/v3-logo.png" /> <a-button type="primary" @click="handleWin1">新視窗1</a-button> <a-button type="primary" @click="handleWin2">新視窗2</a-button> </div></template><script>import {windowClose, windowCreate} from '@/plugins'export default { setup() { const handleWin1 = () => { windowCreate({ title: '我的頁面', route: '/me', width: 1200, height: 750, backgroundColor: '#f9f9f9', resizable: true, modal: true, maximize: true, }) } const handleWin2 = () => { windowCreate({ title: '新聞頁面', route: '/news?id=20210215', width: 960, height: 550, backgroundColor: '#f90', resizable: false, }) } ... return { handleWin1, handleWin2, ... } }}</script>
這樣就實現了透過封裝函式來呼叫新建視窗了。
在開發過程中遇到的一些問題/踩坑。
托盤圖示載入本地圖片失敗?一開始一直以為__dirname指向src目錄,後來經過除錯,原來是指向了dist_electron目錄。
console.log(__dirname)let trayIco = path.join(__dirname, '../static/logo.png')console.log(trayIco)
這樣就能正確獲取到本地圖片了。
後來經過一番倒騰,可以透過下面這段程式碼來禁用掉右鍵選單。
好了,以上基於一些vue3結合electron建立多個窗體的一些分享。
最新評論