vue3+vite+js项目引入electron构建跨平台桌面应用

1.准备工作

① 必要安装node.js、vue、vite、electron、pnpm

        本人用的node版本v18.17.1、vue版本^3.4.19、vite版本^3.2.7、electron版本^35.1.4

② 开发调试打包安装

"devDependencies": {"concurrently": "^9.1.2","electron-builder": "^26.0.12", "electron-devtools-installer": "^4.0.0","vite-plugin-electron": "^0.29.0", "vite-plugin-electron-renderer": "^0.14.6",   "wait-on": "^8.0.3"
}

package.json结构:

{"name": "okyi_admin","private": true,"version": "0.0.1","main": "electron/main.js", // 改动点1"scripts": {"dev": "vite","preview": "vite preview","build": "vite build --mode production",// 改动点2 注意此处端口为5173,在vite.config.js中server下的port启动端口务必保持一致"electron": "wait-on tcp:5173 && electron .","electron:dev": "concurrently -k \"pnpm run dev\" \"pnpm run electron\"","electron:build": "pnpm run build && electron-builder","electron:buildAll": "pnpm run build && electron-builder -wml","postinstall": "electron-builder install-app-deps"},"dependencies": {"@electron/remote": "^2.1.2","@element-plus/icons-vue": "^2.0.9","@types/node": "^18.6.5","@wangeditor/editor": "^5.1.23","@wangeditor/editor-for-vue": "^5.1.12","add": "^2.0.6","animate.css": "^4.1.1","axios": "1.6.0","crypto-js": "^4.2.0","electron-reload": "2.0.0-alpha.1", // 改动点3"element-plus": "2.2.21","js-cookie": "^3.0.1","path-to-regexp": "^6.2.1","pinia": "^2.0.17","pinia-plugin-persist": "^1.0.0","unplugin-element-plus": "^0.4.1","vue": "^3.4.19","vue-router": "^4.1.3","vue3-video-play": "^1.3.2","ws": "^8.14.2","yarn": "^1.22.19"},"devDependencies": {"@vitejs/plugin-vue": "^3.0.0","babel-eslint": "^10.1.0","concurrently": "^9.1.2", // 改动点4"consola": "^2.15.3","electron": "^35.1.4", // 改动点5"electron-builder": "^26.0.12", // 改动点6"electron-devtools-installer": "^4.0.0", // 改动点7"eslint": "^8.25.0","eslint-config-prettier": "^8.5.0","eslint-plugin-html": "^7.1.0","eslint-plugin-prettier": "^4.2.1","eslint-plugin-vue": "^9.6.0","less": "^4.2.0","prettier": "^2.7.1","sass": "^1.55.0","sass-loader": "^13.1.0","unplugin-auto-import": "^0.11.1","unplugin-vue-components": "^0.22.3","vite": "^3.2.7","vite-plugin-electron": "^0.29.0", // 改动点8"vite-plugin-electron-renderer": "^0.14.6", // 改动点9"vite-plugin-style-import": "^2.0.0","wait-on": "^8.0.3" // 改动点10},// 改动点11"build": {"appId": "com.yourcompany.yourapp","productName": "Your App","copyright": "Copyright © 2025","directories": {"output": "release/${version}", // 打包后产物路径"buildResources": "build-electron"},"files": ["dist/**/*","electron/**/*","!**/node_modules/**/*"],"win": {"target": "nsis","icon": "public/icon.ico" // 应用logo路径},"mac": {"target": "dmg","icon": "public/icon.icns", // 应用logo路径"category": "public.app-category.productivity"},"linux": {"target": "AppImage","icon": "public/icon.png" // 应用logo路径},"nsis": {"oneClick": false,"allowToChangeInstallationDirectory": true}},
}

2.vite.config.js中修改如下:

import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import * as path from 'path';
... 其它导入 ...import electron from 'vite-plugin-electron'
import electronRenderer from 'vite-plugin-electron-renderer'export default defineConfig((env) => {const evnMap = loadEnv(env.mode, process.cwd());console.log(`当前运行环境配置信息 evnMap = ${JSON.stringify(evnMap)}`);return {base: './', // 必须设置为相对路径resolve: {alias: {'@': path.resolve(__dirname, 'src'),'@a': path.resolve(__dirname, 'src/assets'),'@u': path.resolve(__dirname, 'src/utils'),'@c': path.resolve(__dirname, 'src/components'),'@api': path.resolve(__dirname, 'src/api'),},},... 其它配置 ...build: {... 其它配置 ...emptyOutDir: false, // 避免electron构建被清空},plugins: [... 其它配置 ...electron({entry: 'electron/main.js',onstart(options) {options.startup(['.', '--no-sandbox']).then(r => {})},vite: {build: {sourcemap: true,outDir: 'dist-electron',},},}),electronRenderer({nodeIntegration: true,}),... 其它配置 ...],... 其它配置 ...server: {open: false, // 调试桌面应用时务必置为falsehost: '0.0.0.0', // ip地址port: 5173, // 启动端口... 其它配置 ...},};
});

3.项目根目录下创建electron目录,并新建main.js、preload.js文件,main.js对应的是package.json中main字段的值

① main.js内容如下:

const { app, BrowserWindow, ipcMain, shell } = require('electron')
const path = require('path')
const isDev = !app.isPackaged// 安全设置
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'let mainWindowasync function createWindow() {mainWindow = new BrowserWindow({width: 1200,height: 800,minWidth: 800,minHeight: 600,show: true,webPreferences: {preload: path.join(__dirname, 'preload.js'),sandbox: true,contextIsolation: true,nodeIntegration: false,webSecurity: false // 启用web安全策略}})// 优雅加载mainWindow.once('ready-to-show', () => {mainWindow.show()if (isDev) {mainWindow.webContents.openDevTools({ mode: 'detach' })}})// 安全策略:阻止外部链接在应用内打开mainWindow.webContents.setWindowOpenHandler(({ url, frameName, features }) => {console.log(`尝试打开: ${url}, 框架名: ${frameName}, 特性: ${features}`)if (!url.startsWith('https://')) {shell.openExternal(url) // 使用外部浏览器打开return { action: 'deny' }}return { action: 'allow' } // 使用桌面应用新窗口形式打开})// 加载应用if (isDev) {require('electron-reload')/*(__dirname, {electron: path.join(__dirname, 'node_modules', '.bin', 'electron'),hardResetMethod: 'exit'})*/// 加载浏览器安全策略// mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {// 	callback({// 		responseHeaders: {// 			...details.responseHeaders,// 			'Content-Security-Policy': [// 				`default-src 'self' 'unsafe-inline' data:;// 				 script-src 'self' 'unsafe-eval' 'unsafe-inline' http:;// 				 connect-src 'self' ws://admin-test-api.ok-yi.com:* http://8.217.215.97:* https://admin-test-api.ok-yi.com:* https://admin-test-api.ok-yi.com:*;// 				 img-src 'self' data: http:;// 				 style-src 'self' 'unsafe-inline';// 				 font-src 'self' data:;`// 			]// 		}// 	})// })await mainWindow.loadURL('http://localhost:5173') // 启动端口5173务必与vite.config.js中保持一致} else {// vue3+vite项目默认构建产物在根目录的dist下await mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))}// 开发工具if (isDev) {const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer')try {await installExtension(VUEJS_DEVTOOLS)} catch (e) {console.error('Vue Devtools failed to install:', e.toString())}}
}// 安全通信通道
ipcMain.handle('get-app-version', () => {return app.getVersion()
})app.whenReady().then(() => {createWindow().then(r => {})
})app.on('window-all-closed', () => {if (process.platform !== 'darwin') app.quit()
})app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) createWindow().then(r => {})
})

② preload.js内容如下:

const { contextBridge, ipcRenderer } = require('electron')// 安全暴露有限的API给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {getAppVersion: () => ipcRenderer.invoke('get-app-version'),// openExternal: (url) => {// 	console.log('openExternal = ', url)// 	ipcRenderer.send('open-external', url)// },platform: process.platform
})

4.在App.vue中新增内容,获取electron主进程暴露给渲染进程的api,内容如下:

<template>
<!--	<el-config-provider :locale="zhCn"><router-view></router-view></el-config-provider>--><div><p>App Version: {{ appVersion }}</p><p>Platform: {{ platform }}</p><button @click="openDocs">Open Docs</button></div>
</template>
<script setup>
// import zhCn from 'element-plus/lib/locale/lang/zh-cn';
import { ref, onMounted } from 'vue'const appVersion = ref('')
const platform = ref('')onMounted(async () => {if (window.electronAPI) {appVersion.value = await window.electronAPI.getAppVersion()platform.value = window.electronAPI.platform}
})const openDocs = () => {if (window.electronAPI) {// window.electronAPI.openExternal('https://electronjs.org/docs')// window.open打开的外部链接会通过electron main.js中mainWindow.webContents.setWindowOpenHandler去过滤是使用窗口形式打开还是浏览器形式打开window.open('https://electronjs.org/docs')} else {window.open('https://electronjs.org/docs', '_blank')}
}
</script><style scoped></style>

5.一切准备就绪,接下来就可以运行跑起来了

确保已执行 pnpm install且成功安装所有依赖,执行命令:pnpm run electron:dev 桌面窗口正常弹出来了同时出现的还有调试工具,如下图:

打包构建使用命令:pnpm run electron:build,本人是用的mac,打包后产物如下图:

结尾:game over!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(51单片机)串口通讯(串口通讯教程)(串口接收发送教程)

前言&#xff1a; 今天有两个项目&#xff0c;分别为&#xff1a; 串口接收: 串口发送&#xff1a; 如上图将文件放在Keli5 中即可&#xff0c;然后烧录在单片机中就行了 烧录软件用的是STC-ISP&#xff0c;不知道怎么安装的可以去看江科大的视频&#xff1a; 【51单片机入门…

《汽车制造技术基础》第一次作业

作业内容 查阅相关资料&#xff0c;谈谈对汽车制造技术的发展的理解。 可以是关于汽车的先进制造技术 或 汽车先进制造技术 与 制造理念的发展趋势 或 汽车先进制造技术对环境与可持续发展的影响等。 以下从技术突破、制造理念转型及环境影响三个维度展开对汽车制造技…

Scala day4(tuple, set and map)

Foreword Hi!! my dear friends, are you lazy at today?? Oh! I am also lazy sometimes, but you will know keep study that’s a right way at last. Now!! let’s start new travel about Scala. Text The all Codes in file day3.scala, like the below program: i…

docker compose搭建博客wordpress

一、前言 docker安装等入门知识见我之前的这篇文章 https://blog.csdn.net/m0_73118788/article/details/146986119?fromshareblogdetail&sharetypeblogdetail&sharerId146986119&sharereferPC&sharesourcem0_73118788&sharefromfrom_link 1.1 docker co…

第二期:[特殊字符] 深入理解MyBatis[特殊字符]MyBatis基础CRUD操作详解[特殊字符]

前言 &#x1f31f; 在掌握了 MyBatis 的基本配置与环境搭建之后&#xff0c;接下来的重点便是深入理解其核心功能——CRUD 操作&#xff08;增删改查&#xff09;。&#x1f4bb; 数据库操作是任何应用开发中不可或缺的一环&#xff0c;而 MyBatis 正是通过灵活的 SQL 映射机…

Java面试黄金宝典46

1. Python 如何写爬虫 定义:Python 爬虫是借助 Python 语言编写程序,模拟浏览器行为向目标网站发送 HTTP 请求,获取网页内容,再通过解析工具提取所需数据的程序。其本质是自动化的数据采集过程。要点: 发送请求:利用requests库发送 HTTP 请求,如 GET、POST 等,获取网页…

建设“大数据智慧招商平台”,助力园区突破招商瓶颈!

在数字经济高速发展的今天&#xff0c;传统招商模式正面临信息不对称、效率低下、匹配不精准等瓶颈。产业园区作为区域经济发展的核心载体&#xff0c;亟需借助智能化手段提升招商效能。构建大数据智慧招商平台&#xff0c;利用大数据、人工智能等技术获取精准招商线索、促进产…

Vue事件修饰符课堂练习

Vue事件修饰符课堂练习 题目‌&#xff1a;基于 Vue 2.0&#xff0c;使用事件修饰符 .stop、.prevent、.capture、.self 和 .once&#xff0c;为按钮绑定 click 事件&#xff0c;并展示每个修饰符的作用。 要求‌&#xff1a; 创建一个 Vue 实例&#xff0c;并绑定到一个 HT…

【C#】线程回调

在 C# 中&#xff0c;线程回调是一种常见的编程模式&#xff0c;用于在线程完成任务后执行某些操作。通过使用 Thread 类或其他更高层次的并发工具&#xff08;如 Task&#xff09;&#xff0c;可以实现线程回调的功能。 回调机制 特点 直接性&#xff1a;回调通常是通过委托…

【C++游戏引擎开发】第14篇:视图空间与相机坐标系

一、视图空间的基础数学框架 1.1 齐次坐标与变换矩阵 三维坐标系变换采用44齐次坐标矩阵,其通用形式为: M = [ A 3 3 b 3 1 0 1 3 1 ] \mathbf{M} = \begin{bmatrix} \mathbf{A}_{33} & \mathbf{b}_{31} \\ \mathbf{0}_{13} & 1 \end{bmatrix} M=[A33​013​​…

【大模型理论篇】关于生成式模型中联合分布概率学习必要性以及GPT是生成式模型的讨论

1. 背景 之前我们在《生成式模型与判别式模型对比(涉及VAE、CRF的数学原理详述)》以及《生成式模型算法原理深入浅出&#xff08;涉及Stable Diffusion、生成对抗网络、高斯混合模型、隐马尔可夫模型、朴素贝叶斯等算法原理分析及生成式模型解释&#xff09;》中&#xff0c;我…

DIP支付方式改革下各种疾病医疗费用的影响以及分析方法研究综述

DIP支付方式改革下各种疾病医疗费用的影响以及分析方法研究综述 摘要 本文综述了DIP支付方式改革对不同疾病医疗费用的影响及其分析方法&#xff0c;通过分析12篇相关文献&#xff0c;探讨了DIP支付方式在控制医疗费用、优化费用结构、提升医疗服务效率等方面的作用及其局限性…

嵌入式硬件篇---单片机周期

文章目录 前言 前言 在单片机中&#xff0c;时序控制是其执行指令和协调外设的核心基础。以下是单片机中常见的各种周期及其详细说明&#xff0c;以层次结构展开&#xff1a; 时钟周期&#xff08;Clock Cycle&#xff09; 定义&#xff1a; 时钟周期是单片机的最小时间单位&a…

游戏引擎学习第221天:(实现多层次过场动画)

资产: intro_art.hha 已发布 在下载页面&#xff0c;你会看到一个新的艺术包。你将需要这个艺术包来进行接下来的开发工作。这个艺术包是由一位艺术家精心制作并打包成我们设计的格式&#xff0c;旨在将这些艺术资源直接应用到游戏中。它包含了许多我们会在接下来的直播中使用…

【3GPP核心网】【5G】精讲5G系统的策略和计费控制框架

1. 欢迎大家订阅和关注,精讲3GPP通信协议(2G/3G/4G/5G/IMS)知识点,专栏会持续更新中.....敬请期待! 目录 1. 系统架构 1.1 非漫游架构 1.2 漫游架构 1.3 支持Rx接口 2. 服务化接口及参考点 2.1 PCF 与 AF 间接口 2.2 PCF与SMF间接口 2.3 PCF与AMF间接口 2.4 V-PC…

榕壹云门店管理系统:基于Spring Boot+Mysql+UniApp的智慧解决方案

项目背景&#xff1a;数字化赋能服务行业&#xff0c;破解传统门店管理痛点 在消费升级与数字化转型浪潮下&#xff0c;传统服务行业&#xff08;如美容、美发、美甲、采耳等&#xff09;面临诸多管理挑战&#xff1a;会员流失率高、预约排班混乱、员工绩效统计低效、数据孤岛等…

开发效率提升200%——cursor

cursor带来的编程"革命" 高级语言编程转为"自然语言编程"借助cursor&#xff0c;直接超越初级后台开发、超越初级前端开发、超越初级测试、超越初级UI&#xff0c;产研一体linux命令只用学不用记&#xff0c;语言描述就是命令给一个表结构流程提示词&…

UE4 踩坑记录

1、Using git status to determine working set for adaptive non-unity build 我删除了一个没用的资源&#xff0c;结果就报这个错&#xff0c;原因就是这条命令导致的&#xff0c; 如果这个项目是git项目&#xff0c; ue编译时会优先通过 git status检查哪些文件被修改&#…

蓝桥杯 2025 C++组 省 B 题解

可分解的正整数 算法&#xff1a;思维 因为可以有负数 所以除了1以外的任何数都可以构造 当这个数为x构造方法为 -(x-1) -(x-2) -(x-3) ....-1 0 1...x-3 x-2 x-1 x 除了x&#xff0c;x以前的数都会被负数抵消 #include <bits/stdc.h> #define ll long long ll a…

docker创建容器添加启动--restart选项

一、通过 Docker 命令直接修改已启动的容器&#xff08;推荐-已验证&#xff09; 操作步骤&#xff1a; 1.执行更新命令&#xff1a; docker update --restartalways <容器名或ID>此命令会将容器的重启策略调整为 always&#xff08;无论容器以何种状态退出&#xff0…