经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
基于electron25+vite4创建多窗口|vue3+electron25新开模态窗体
来源:cnblogs  作者:xiaoyan2017  时间:2023/5/30 10:22:09  对本文有异议

在写这篇文章的时候,查看了下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

  1. // In the main process.
  2. const { BrowserWindow } = require('electron')
  3. const win = new BrowserWindow({ width: 800, height: 600 })
  4. // Load a remote URL
  5. win.loadURL('https://github.com')
  6. // Or load a local HTML file
  7. win.loadFile('index.html')

如果每次都new一个BrowserWindow窗口,显得有些笨拙且复杂。今天要分享的是封装BrowserWindow方法,只需传入配置参数,即可快速生成一个独立窗口。

  1. createWin({
  2. title: '关于About.vue',
  3. route: '/about',
  4. width: 600,
  5. height: 400,
  6. background: '#fafffa',
  7. resize: true
  8. })

新建一个windows/index.js文件。

  1. /**
  2. * 封装多窗口管理器
  3. * @author YXY
  4. */
  5. const { app, BrowserWindow, ipcMain } = require('electron')
  6. const { join } = require('path')
  7. process.env.ROOT = join(__dirname, '../../')
  8. const isDevelopment = process.env.NODE_ENV == 'development'
  9. // const winURL = isDevelopment ? 'http://localhost:3000/' : join(__dirname, 'dist/index.html')
  10. const winURL = isDevelopment ? process.env.VITE_DEV_SERVER_URL : join(process.env.ROOT, 'dist/index.html')
  11. // 配置参数
  12. const defaultConfig = {
  13. id: null, // 窗口唯一id
  14. background: '#fff', // 背景色
  15. route: '', // 路由地址url
  16. title: '', // 标题
  17. data: null, // 传入数据参数
  18. width: '', // 窗口宽度
  19. height: '', // 窗口高度
  20. minWidth: '', // 窗口最小宽度
  21. minHeight: '', // 窗口最小高度
  22. x: '', // 窗口相对于屏幕左侧坐标
  23. y: '', // 窗口相对于屏幕顶端坐标
  24. resize: true, // 是否支持缩放
  25. maximize: false, // 最大化窗口
  26. isMultiWin: false, // 是否支持多开窗口
  27. isMainWin: false, // 是否主窗口
  28. parent: '', // 父窗口(需传入父窗口id)
  29. modal: false, // 模态窗口(模态窗口是浮于父窗口上,禁用父窗口)
  30. alwaysOnTop: false // 置顶窗口
  31. }
  32. class MultiWindows {
  33. constructor() {
  34. // 主窗口
  35. this.mainWin = null
  36. // 窗口组
  37. this.winLs = {}
  38. // ...
  39. }
  40. winOpts() {
  41. return {
  42. // 窗口图标
  43. icon: join(process.env.ROOT, 'resource/shortcut.ico'),
  44. backgroundColor: '#fff',
  45. autoHideMenuBar: true,
  46. titleBarStyle: 'hidden',
  47. width: 1000,
  48. height: 640,
  49. resizable: true,
  50. minimizable: true,
  51. maximizable: true,
  52. frame: false,
  53. show: false,
  54. webPreferences: {
  55. contextIsolation: true, // 启用上下文隔离(为了安全性)(默认true)
  56. // nodeIntegration: false, // 启用Node集成(默认false)
  57. preload: join(process.env.ROOT, 'resource/preload.js'),
  58. // devTools: true,
  59. // webSecurity: false
  60. }
  61. }
  62. }
  63. // 创建新窗口
  64. createWin(options) {
  65. const args = Object.assign({}, defaultConfig, options)
  66. console.log(args)
  67. // 判断窗口是否存在
  68. for(let i in this.winLs) {
  69. if(this.getWin(i) && this.winLs[i].route === args.route && !this.winLs[i].isMultiWin) {
  70. this.getWin(i).focus()
  71. return
  72. }
  73. }
  74. let opt = this.winOpts()
  75. if(args.parent) {
  76. opt.parent = this.getWin(args.parent)
  77. }
  78. if(typeof args.modal === 'boolean') opt.modal = args.modal
  79. if(typeof args.resize === 'boolean') opt.resizable = args.resize
  80. if(typeof args.alwaysOnTop === 'boolean') opt.alwaysOnTop = args.alwaysOnTop
  81. if(args.background) opt.backgroundColor = args.background
  82. if(args.width) opt.width = args.width
  83. if(args.height) opt.height = args.height
  84. if(args.minWidth) opt.minWidth = args.minWidth
  85. if(args.minHeight) opt.minHeight = args.minHeight
  86. if(args.x) opt.x = args.x
  87. if(args.y) opt.y = args.y
  88. console.log(opt)
  89. // 创建窗口对象
  90. let win = new BrowserWindow(opt)
  91. // 是否最大化
  92. if(args.maximize && args.resize) {
  93. win.maximize()
  94. }
  95. this.winLs[win.id] = {
  96. route: args.route, isMultiWin: args.isMultiWin
  97. }
  98. args.id = win.id
  99. // 加载页面
  100. let $url
  101. if(!args.route) {
  102. if(process.env.VITE_DEV_SERVER_URL) {
  103. // 打开开发者调试工具
  104. // win.webContents.openDevTools()
  105. $url = process.env.VITE_DEV_SERVER_URL
  106. }else {
  107. $url = winURL
  108. }
  109. }else {
  110. $url = `${winURL}#${args.route}`
  111. }
  112. win.loadURL($url)
  113. /*if(process.env.VITE_DEV_SERVER_URL) {
  114. win.loadURL($url)
  115. }else {
  116. win.loadFile($url)
  117. }*/
  118. win.webContents.openDevTools()
  119. win.once('ready-to-show', () => {
  120. win.show()
  121. })
  122. win.on('close', () => win.setOpacity(0))
  123. // 初始化渲染进程
  124. win.webContents.on('did-finish-load', () => {
  125. // win.webContents.send('win-loaded', '加载完成~!')
  126. win.webContents.send('win-loaded', args)
  127. })
  128. }
  129. // 获取窗口
  130. getWin(id) {
  131. return BrowserWindow.fromId(Number(id))
  132. }
  133. // 获取全部窗口
  134. getAllWin() {
  135. return BrowserWindow.getAllWindows()
  136. }
  137. // 关闭全部窗口
  138. closeAllWin() {
  139. try {
  140. for(let i in this.winLs) {
  141. if(this.getWin(i)) {
  142. this.getWin(i).close()
  143. }else {
  144. app.quit()
  145. }
  146. }
  147. } catch (error) {
  148. console.log(error)
  149. }
  150. }
  151. // 开启主进程监听
  152. ipcMainListen() {
  153. // 设置标题
  154. ipcMain.on('set-title', (e, data) => {
  155. const webContents = e.sender
  156. const wins = BrowserWindow.fromWebContents(webContents)
  157. wins.setTitle(data)
  158. // const wins = BrowserWindow.getFocusedWindow()
  159. // wins.setTitle('啦啦啦')
  160. })
  161. // 是否最大化(方法一)
  162. /*ipcMain.on('isMaximized', e => {
  163. const win = BrowserWindow.getFocusedWindow()
  164. e.sender.send('mainReplay', win.isMaximized())
  165. })*/
  166. // 是否最大化(方法二)
  167. ipcMain.handle('isMaximized', (e) => {
  168. const win = BrowserWindow.getFocusedWindow()
  169. return win.isMaximized()
  170. })
  171. ipcMain.on('min', e => {
  172. const win = BrowserWindow.getFocusedWindow()
  173. win.minimize()
  174. })
  175. ipcMain.handle('max2min', e => {
  176. const win = BrowserWindow.getFocusedWindow()
  177. if(win.isMaximized()) {
  178. win.unmaximize()
  179. return false
  180. }else {
  181. win.maximize()
  182. return true
  183. }
  184. })
  185. ipcMain.on('close', (e, data) => {
  186. // const wins = BrowserWindow.getFocusedWindow()
  187. // wins.close()
  188. this.closeAllWin()
  189. })
  190. // ...
  191. }
  192. }
  193. module.exports = MultiWindows

在主进程入口background.js文件引入封装窗口。

  1. const { app, BrowserWindow, ipcMain } = require('electron')
  2. const { join } = require('path')
  3. const MultiWindows = require('./src/windows')
  4. // 屏蔽安全警告
  5. // ectron Security Warning (Insecure Content-Security-Policy)
  6. process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
  7. const createWindow = () => {
  8. let window = new MultiWindows()
  9. window.createWin({isMainWin: true})
  10. window.ipcMainListen()
  11. }
  12. app.whenReady().then(() => {
  13. createWindow()
  14. app.on('activate', () => {
  15. if (BrowserWindow.getAllWindows().length === 0) createWindow()
  16. })
  17. })
  18. app.on('window-all-closed', () => {
  19. if (process.platform !== 'darwin') app.quit()
  20. })

在主进程中做一个ipcMain监听,用来创建独立窗口。

  1. ipcMain.on('win-create', (event, args) => this.createWin(args))

新建windows/action.js文件,处理渲染器进程到主进程的异步通信,可以发送同步或异步的消息到主进程,也可以接收主进程发送的消息。

  1. /**
  2. * 创建新窗口
  3. * @param {object} args | {width: 640, height: 480, route: '/home'}
  4. */
  5. export function createWin(args) {
  6. window.electronAPI.send('win-create', args)
  7. }
  8. /**
  9. * 设置窗口
  10. * @param {string} type | 'show'/'hide'/'close'/'min'/'max'/'max2min'/'restore'/'reload'
  11. * @param {number} id
  12. */
  13. export function setWin(type, id) {
  14. window.electronAPI.send('win-' + type, id)
  15. }
  16. /**
  17. * 创建登录窗口
  18. */
  19. export function loginWin() {
  20. createWin({
  21. isMainWin: true,
  22. title: '登录',
  23. route: '/login',
  24. width: 550,
  25. height: 320,
  26. resize: false,
  27. alwaysOnTop: true,
  28. })
  29. }

在vue页面中调用上面封装的方法。

  1. <template>
  2. <div class="home">
  3. ...
  4. <Button type="success" @click="openWin">打开Manage窗口(设置parent)</Button>
  5. <Button type="success" @click="openWin1">打开Me窗口(设置resizable/isMultiWin)</Button>
  6. <Button type="success" @click="openWin2">打开User窗口</Button>
  7. </div>
  8. </template>
  9.  
  10. <script>
  11. import { winCfg, createWin } from '@/windows/action'
  12. export default {
  13. name: 'Home',
  14. setup() {
  15. const openWin = () => {
  16. MessageBox.confirm('提示', '确定打开Manage页面吗? 【设置parent属性】', {
  17. callback: action => {
  18. if(action == 'confirm') {
  19. createWin({
  20. title: 'Manage.vue',
  21. route: '/manage',
  22. width: 600,
  23. height: 400,
  24. background: '#09f',
  25. parent: winCfg.window.id,
  26. // modal: true
  27. })
  28. }else if(action == 'cancel') {
  29. Message.info('您已取消!')
  30. }
  31. }
  32. })
  33. }
  34. const openWin1 = () => {
  35. // 左上角
  36. // let posX = 0
  37. // let posY = 0
  38.  
  39. // 右下角
  40. let posX = window.screen.availWidth - 850
  41. let posY = window.screen.availHeight - 600
  42. MessageBox.confirm('提示', '确定打开Me页面吗?', {
  43. callback: action => {
  44. if(action == 'confirm') {
  45. createWin({
  46. title: 'Me.vue',
  47. route: '/me?name=Andy',
  48. width: 850,
  49. height: 600,
  50. x: posX,
  51. y: posY,
  52. background: 'yellow',
  53. resize: false,
  54. isMultiWin: true,
  55. maximize: true
  56. })
  57. }else if(action == 'cancel') {
  58. Message.info('您已取消!')
  59. }
  60. }
  61. })
  62. }
  63. const openWin2 = () => {
  64. MessageBox.confirm('提示', '确定打开User页面吗?', {
  65. callback: action => {
  66. if(action == 'confirm') {
  67. createWin({
  68. title: 'User.vue',
  69. route: '/user',
  70. width: 700,
  71. height: 550,
  72. minWidth: 300,
  73. minHeight: 300,
  74. data: {
  75. name: 'Andy',
  76. age: 20
  77. },
  78. background: 'green',
  79. isMultiWin: true
  80. })
  81. }else if(action == 'cancel') {
  82. Message.info('您已取消!')
  83. }
  84. }
  85. })
  86. }
  87. // ...
  88.  
  89. return {
  90. openWin,
  91. openWin1,
  92. openWin2,
  93. // ...
  94. }
  95. }
  96. }
  97. </script>

设置 frame: false 创建无边框窗口。

设置 -webkit-app-region: drag 来实现自定义拖拽区域。设置后的按钮操作无法响应其它事件,只需设置 -webkit-app-region: no-drag 即可实现响应事件。

electron+vite提供的一些环境变量。

  1. process.env.NODE_ENV
  2. process.env.VITE_DEV_SERVER_URL

在开发环境,加载vite url,生产环境,则加载vite build出来的html。

Ok,综上就是electron25+vite4结合构建跨端应用的一些分享,希望对大家有所帮助哈~~

 

原文链接:https://www.cnblogs.com/xiaoyan2017/p/17442502.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号