electron27+react18集成搭建跨平台应用|electron窗口多开

本文主要是介绍electron27+react18集成搭建跨平台应用|electron窗口多开,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于Electron27集成React18创建一个桌面端exe程序。

electron27-vite4-react18基于electron27结合vite4构建工具快速创建react18跨端应用实践。

在这里插入图片描述

版本列表

"vite": "^4.4.5"
"react": "^18.2.0"
"electron": "^27.0.1"
"electron-builder": "^24.6.4"
"vite-plugin-electron": "^0.14.1"

快速创建react18项目

这里选择使用vite.js构建工具来快速创建一个react18项目。

yarn create vite electron-vite4-react18
// 选择创建react模板
cd electron-vite4-react18
yarn install
yarn dev

在这里插入图片描述
这样一个简单的react18项目就已经创建完毕了。

安装electron依赖包

注意:electron依赖安装在 devDependencies 里面。

// 安装electron
yarn add -D electron
// 安装electron-builder 用于构建打包可安装exe程序
yarn add -D electron-builder
// 安装electron-devtools-installer 用于开发调试electron项目
yarn add -D electron-devtools-installer

安装的过程可能比较慢,甚至卡顿,可以考虑切换taobao镜像。

接下来还需要安装一个vite集成electron插件。

yarn add -D vite-plugin-electron

创建electron主进程

在项目根目录下新建一个主进程文件electron-main.js。

在这里插入图片描述

const { app, BrowserWindow } = require('electron')const MultiWindow = require('./src/windows')const createWindow = () => {let win = new MultiWindow()win.createWin({ isMainWin: true })
}app.whenReady().then(() => {createWindow()app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) createWindow()})
})app.on('window-all-closed', () => {if (process.platform !== 'darwin') app.quit()
})

配置vite.config.js主进程入口。

import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import electron from 'vite-plugin-electron'
import { resolve } from 'path'
import { parseEnv } from './src/utils/env'// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {const viteEnv = loadEnv(mode, process.cwd())const env = parseEnv(viteEnv)return {plugins: [react(),electron({entry: 'electron-main.js',})],esbuild: {// 打包去除 console.log 和 debuggerdrop: env.VITE_DROP_CONSOLE && command === 'build' ? ["console", "debugger"] : []},/* 开发服务器配置 */server: {// 端口port: env.VITE_PORT,// 代理配置proxy: {// ...}},resolve: {// 设置别名alias: {'@': resolve(__dirname, 'src'),'@assets': resolve(__dirname, 'src/assets'),'@components': resolve(__dirname, 'src/components'),'@views': resolve(__dirname, 'src/views')}}}
})

简单配置下package.json

修改package.json文件,加入"main": "electron-main.js"入口配置,并且需要去掉 “type”: “module”

接下来运行命令 yarn electron:serve就可以启动桌面端项目了。

在这里插入图片描述

之前也有分享过electron+vite4+vue3创建桌面端项目的一些分享,里面有打包的一些配置,感兴趣可以去看看。

https://blog.csdn.net/yanxinyun1990/article/details/130944508

https://blog.csdn.net/yanxinyun1990/article/details/130977430

electron创建无边框窗口拖拽

在这里插入图片描述
创建窗口配置 frame: false 即可创建一个无系统边框的窗体。

在这里插入图片描述
css设置 -webkit-app-region: drag 来实现拖拽区域。设置 -webkit-app-region: no-drag 取消拖拽响应。

自定义窗口最大化/最小化/关闭功能。

import { useState, useContext } from 'react'
import { Modal } from '@arco-design/web-react'
import { setWin } from '@/windows/action'function WinBtn(props) {const {color = '#fff',minimizable = true,maximizable = true,closable = true,zIndex = 2023,children} = propsconst [hasMaximized, setHasMaximized] = useState(false)window.electronAPI.invoke('win__isMaximized').then(res => {setHasMaximized(res)})window.electronAPI.receive('win__hasMaximized', (e, res) => {setHasMaximized(res)})// 最小化const handleWinMin = () => {window.electronAPI.send("win__minimize")}// 最大化/还原const handleWinMax2Min = () => {window.electronAPI.invoke("win__max2min").then(res => {console.log(res)setHasMaximized(res)})}// 关闭const handleWinClose = () => {if(window.config.isMainWin) {Modal.confirm({title: '提示',content: <div style={{ textAlign: 'center' }}>是否最小化至托盘,不退出程序?</div>,okButtonProps: {status: 'warning'},style: {width: 360},cancelText: '最小化至托盘',okText: '残忍退出',onOk: () => {setWin('close')},onCancel: () => {setWin('hide', window.config.id)}})}else {setWin('close', window.config.id)}}return (<><div className="vui__macbtn flexbox flex-alignc" style={{zIndex: zIndex}}><div className="vui__macbtn-groups flexbox flex-alignc" style={{color: color}}>{ JSON.parse(minimizable) && <a className="mbtn min" title="最小化" onClick={handleWinMin}><svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#995700" d="M8.048,4.001c0.163,0.012 0.318,0.054 0.459,0.137c0.325,0.191 0.518,0.559 0.49,0.934c-0.007,0.097 -0.028,0.192 -0.062,0.283c-0.04,0.105 -0.098,0.204 -0.171,0.29c-0.083,0.098 -0.185,0.181 -0.299,0.24c-0.131,0.069 -0.271,0.103 -0.417,0.114c-2.031,0.049 -4.065,0.049 -6.096,0c-0.163,-0.012 -0.318,-0.054 -0.459,-0.137c-0.325,-0.191 -0.518,-0.559 -0.49,-0.934c0.007,-0.097 0.028,-0.192 0.062,-0.283c0.04,-0.105 0.098,-0.204 0.171,-0.29c0.083,-0.098 0.185,-0.181 0.299,-0.24c0.131,-0.069 0.271,-0.103 0.417,-0.114c2.031,-0.049 4.065,-0.049 6.096,0Z"></path></svg></a> }{ JSON.parse(maximizable) &&<a className="mbtn max" title={hasMaximized ? '向下还原' : '最大化'} onClick={handleWinMax2Min}>{hasMaximized ?<svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M5,10c0,0 0,-2.744 0,-4.167c0,-0.221 -0.088,-0.433 -0.244,-0.589c-0.156,-0.156 -0.368,-0.244 -0.589,-0.244c-1.423,0 -4.167,0 -4.167,0l5,5Z"></path><path fill="#006400" d="M5,0c0,0 0,2.744 0,4.167c0,0.221 0.088,0.433 0.244,0.589c0.156,0.156 0.368,0.244 0.589,0.244c1.423,0 4.167,0 4.167,0l-5,-5Z"></path></svg>:<svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M2,3c0,0 0,2.744 0,4.167c0,0.221 0.088,0.433 0.244,0.589c0.156,0.156 0.368,0.244 0.589,0.244c1.423,0 4.167,0 4.167,0l-5,-5Z"></path><path fill="#006400" d="M8,7c0,0 0,-2.744 0,-4.167c0,-0.221 -0.088,-0.433 -0.244,-0.589c-0.156,-0.156 -0.368,-0.244 -0.589,-0.244c-1.423,0 -4.167,0 -4.167,0l5,5Z"></path></svg>}</a>}{ JSON.parse(closable) && <a className="mbtn close" title="关闭" onClick={handleWinClose}><svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M5,3.552c0.438,-0.432 0.878,-0.861 1.322,-1.287c0.049,-0.044 0.101,-0.085 0.158,-0.119c0.149,-0.091 0.316,-0.137 0.49,-0.146c0.04,0 0.04,0 0.08,0.001c0.16,0.011 0.314,0.054 0.453,0.135c0.08,0.046 0.154,0.104 0.218,0.171c0.252,0.262 0.342,0.65 0.232,0.996c-0.045,0.141 -0.121,0.265 -0.218,0.375c-0.426,0.444 -0.855,0.884 -1.287,1.322c0.432,0.438 0.861,0.878 1.287,1.322c0.097,0.11 0.173,0.234 0.218,0.375c0.04,0.126 0.055,0.26 0.043,0.392c-0.011,0.119 -0.043,0.236 -0.094,0.344c-0.158,0.327 -0.49,0.548 -0.852,0.566c-0.106,0.005 -0.213,-0.007 -0.315,-0.035c-0.156,-0.043 -0.293,-0.123 -0.413,-0.229c-0.444,-0.426 -0.884,-0.855 -1.322,-1.287c-0.438,0.432 -0.878,0.861 -1.322,1.287c-0.11,0.097 -0.234,0.173 -0.375,0.218c-0.126,0.04 -0.26,0.055 -0.392,0.043c-0.119,-0.011 -0.236,-0.043 -0.344,-0.094c-0.327,-0.158 -0.548,-0.49 -0.566,-0.852c-0.005,-0.106 0.007,-0.213 0.035,-0.315c0.043,-0.156 0.123,-0.293 0.229,-0.413c0.426,-0.444 0.855,-0.884 1.287,-1.322c-0.432,-0.438 -0.861,-0.878 -1.287,-1.322c-0.106,-0.12 -0.186,-0.257 -0.229,-0.413c-0.025,-0.089 -0.037,-0.182 -0.036,-0.275c0.004,-0.363 0.211,-0.704 0.532,-0.874c0.13,-0.069 0.272,-0.105 0.418,-0.115c0.04,-0.001 0.04,-0.001 0.08,-0.001c0.174,0.009 0.341,0.055 0.49,0.146c0.057,0.034 0.109,0.075 0.158,0.119c0.444,0.426 0.884,0.855 1.322,1.287Z"></path></svg></a> }<i className="mr-10"></i>{ children }</div><div className="vui__mactitle">{window.config.title || '首页'}</div></div></>)
}export default WinBtn

electron创建托盘功能

在这里插入图片描述

/*** Electron多窗口管理器* @author Andy  Q:282310962*/const { app, BrowserWindow, ipcMain, Menu, Tray, dialog, globalShortcut } = require('electron')
// const { loadEnv } = require('vite')
const { join } = require('path')// 根目录路径
process.env.ROOT = join(__dirname, '../../')const isDev = process.env.NODE_ENV === 'development'
// const winURL = isDev ? 'http://localhost:3000/' : join(__dirname, 'dist/index.html')
const winURL = isDev ? process.env.VITE_DEV_SERVER_URL : join(process.env.ROOT, 'dist/index.html')class MultiWindow {constructor() {// 主窗口对象this.main = null// 窗口组this.group = {}// 托盘图标this.tray = nullthis.flashTimer = nullthis.trayIco1 = join(process.env.ROOT, 'resource/tray.ico')this.trayIco2 = join(process.env.ROOT, 'resource/tray-empty.ico')// 监听ipcMain事件this.listenIpc()// 创建系统托盘this.createTray()}// 系统配置参数winOptions() {return {// 窗口图标icon: join(process.env.ROOT, 'resource/shortcut.ico'),backgroundColor: '#fff',autoHideMenuBar: true,titleBarStyle: 'hidden',width: 900,height: 600,resizable: true,minimizable: true,maximizable: true,frame: false, // 设置为 false 时可以创建一个无边框窗口 默认值为 trueshow: false, // 窗口是否在创建时显示webPreferences: {contextIsolation: true, // 启用上下文隔离(为了安全性)(默认true)nodeIntegration: false, // 启用Node集成(默认false)preload: join(process.env.ROOT, 'electron-preload.js')}}}// 创建新窗口createWin(options) {// ...}// ...// 主进程监听事件listenIpc() {// 创建新窗体ipcMain.on('win-create', (event, args) => this.createWin(args))// ...// 托盘图标闪烁ipcMain.on('win__flashTray', (event, bool) => this.flashTray(bool))// 屏幕截图ipcMain.on('win__setCapture', () => {// ...})}// 创建系统托盘图标createTray() {console.log(__dirname)console.log(join(process.env.ROOT, 'resource/tray.ico'))const trayMenu = Menu.buildFromTemplate([{label: '打开主界面',icon: join(process.env.ROOT, 'resource/home.png'),click: () => {try {for(let i in this.group) {let win = this.getWin(i)if(!win) return// 是否主窗口if(this.group[i].isMainWin) {if(win.isMinimized()) win.restore()win.show()}}} catch (error) {console.log(error)}}},{label: '设置中心',icon: join(process.env.ROOT, 'resource/setting.png'),click: () => {for(let i in this.group) {let win = this.getWin(i)if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_SETTING', value: null })}},},{label: '锁屏',click: () => null,},{label: '关闭托盘闪烁',click: () => {this.flashTray(false)}},{type: 'separator'},/* {label: '重启',click: () => {// app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })// app.exit(0)app.relaunch()app.quit()}}, */{label: '关于',click: () => {for(let i in this.group) {let win = this.getWin(i)if(win) win.webContents.send('win__ipcData', { type: 'CREATE_WIN_ABOUT', value: null })}}},{label: '关闭应用并退出',icon: join(process.env.ROOT, 'resource/logout.png'),click: () => {dialog.showMessageBox(this.main, {title: '询问',message: '确定要退出应用程序吗?',buttons: ['取消', '最小化托盘', '退出应用'],type: 'error',noLink: false,  // true传统按钮样式  false链接样式cancelId: 0}).then(res => {console.log(res)const index = res.responseif(index == 0) {console.log('取消')}if(index == 1) {console.log('最小化托盘')for(let i in this.group) {let win = this.getWin(i)if(win) win.hide()}}else if(index == 2) {console.log('退出应用')try {for(let i in this.group) {let win = this.getWin(i)if(win) win.webContents.send('win__ipcData', { type: 'WIN_LOGOUT', value: null })}// app.quit 和 app.exit(0) 都可退出应用。// 前者可以被打断并触发一些事件,而后者将强制应用程序退出而不触发任何事件或允许应用程序取消操作。app.quit()} catch (error) {console.log(error)}}})}}])this.tray = new Tray(this.trayIco1)this.tray.setContextMenu(trayMenu)this.tray.setToolTip(app.name)this.tray.on('double-click', () => {console.log('double clicked')})// 开启托盘闪烁// this.flashTray(true)}// 托盘图标闪烁flashTray(flash) {let hasIco = falseif(flash) {if(this.flashTimer) returnthis.flashTimer = setInterval(() => {this.tray.setImage(hasIco ? this.trayIco1 : this.trayIco2)hasIco = !hasIco}, 500)}else {if(this.flashTimer) {clearInterval(this.flashTimer)this.flashTimer = null}this.tray.setImage(this.trayIco1)}}// 销毁托盘图标destoryTray() {this.flashTray(false)this.tray.destroy()this.tray = null}
}module.exports = MultiWindow

综上就是electron27+react18创建跨端桌面应用的一些分享,希望能喜欢~~

https://blog.csdn.net/yanxinyun1990/article/details/133880077

https://blog.csdn.net/yanxinyun1990/article/details/132825719

在这里插入图片描述

这篇关于electron27+react18集成搭建跨平台应用|electron窗口多开的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/293928

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

AI行业应用(不定期更新)

ChatPDF 可以让你上传一个 PDF 文件,然后针对这个 PDF 进行小结和提问。你可以把各种各样你要研究的分析报告交给它,快速获取到想要知道的信息。https://www.chatpdf.com/

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。