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,打包后產物如下圖: