当前位置: 首页 > news >正文

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, // 调试桌面应用时务必置为false
			host: '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 mainWindow

async 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!!!

相关文章:

  • Excel 自动执行全局宏
  • 项目进度延误的十大原因及应对方案
  • 4-10记录(
  • 聊天室项目Day3之服务器的http的get和post回复实现
  • 软件信息安全性测试如何进行?有哪些注意事项?
  • 神经网络入门—自定义神经网络续集
  • 2. 单词个数统计
  • WPS JS宏编程教程(从基础到进阶)-- 第六部分:JS集合与映射在 WPS 的应用
  • 关于使用@Slf4j后引入log,idea标红解决办法
  • Linux | I.MX6ULL外设功能验证(11)
  • FreeRTOS项目工程完善指南:STM32F103C8T6系列
  • 【结合vue源码,分析vue2及vue3的数据绑定实现原理】
  • 【力扣hot100题】(083)零钱兑换
  • Redis 持久化机制详解:RDB/AOF 过程、优缺点及配置。Redis持久化中的Fork与Copy-on-Write技术解析。
  • android studio 2022打开了v1 签名但是生成的apk没有v1签名问题
  • C# 组件的使用方法
  • Python proteinflow 库介绍
  • Java中List方法的使用详解
  • ​​大数据量统计优化方案(日/月/年统计场景)​
  • WORD 中批量将植物拉丁名替换为斜体
  • 网站维护开发合同/网址注册
  • pdf做电子书下载网站/市场营销证书含金量
  • 优质校建设专题网站/网站免费搭建平台
  • 旅游网站建设目的/今日的重大新闻
  • 网站做外链软件/关键词检索
  • 公司网站重新备案/aso优化渠道