1. 项目概述
作为一名长期奋战在前端开发一线的工程师,我最近在重构公司内部工具时选择了Electron+Vue3+TS的技术栈。这个组合既能利用Web技术快速开发界面,又能获得原生应用的能力,特别适合需要跨平台部署的管理系统。本文将分享从零搭建到打包发布的完整过程,包含我在实际项目中积累的配置技巧和避坑经验。
2. 环境准备与项目初始化
2.1 工具链选择
现代前端开发已经离不开高效的构建工具,这里我推荐使用以下组合:
- pnpm:比npm/yarn更快的依赖管理工具,节省磁盘空间
- @quick-start/electron:官方维护的脚手架,集成Vite和TypeScript
- Volar:Vue3官方推荐的VSCode插件,提供完美的TS支持
安装pnpm(如果尚未安装):
bash复制npm install -g pnpm
2.2 项目创建详解
执行脚手架命令时,有几个关键选项需要注意:
bash复制pnpm create @quick-start/electron my-electron-app --template vue-ts
交互式配置中,我建议这样选择:
- Add auto-updater?:如果是商业项目选Yes,内部工具选No
- Enable download mirror proxy?:在国内开发强烈建议选Yes
- Add Vue Router?:根据项目复杂度决定,简单工具可不加
提示:如果安装过程中卡在electron下载阶段,可以按Ctrl+C终止后,先设置镜像源再重试:
bash复制pnpm config set electron_mirror https://npmmirror.com/mirrors/electron/
3. 项目结构与核心配置
3.1 目录架构解析
生成的目录结构中,有几个关键位置需要特别关注:
code复制my-electron-app/
├── src/
│ ├── main/ # 主进程代码(Node.js环境)
│ │ ├── index.ts # 应用入口文件
│ │ └── ipc.ts # IPC通信封装
│ ├── preload/ # 安全隔离层脚本
│ └── renderer/ # Vue应用(浏览器环境)
│ └── src/
│ ├── assets/
│ ├── components/
│ ├── App.vue
│ └── main.ts
├── electron.vite.config.ts # 主配置
└── package.json
3.2 配置合并技巧
在electron.vite.config.ts中,我们需要特别注意多环境配置的合并。这是我的推荐配置:
typescript复制import { defineConfig, mergeConfig } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
const commonConfig = {
plugins: [
vue(),
AutoImport({
imports: ['vue', 'vue-router'],
resolvers: [ElementPlusResolver()],
dts: 'src/auto-imports.d.ts'
}),
Components({
resolvers: [ElementPlusResolver()],
dts: 'src/components.d.ts'
})
]
}
export default defineConfig({
main: mergeConfig(commonConfig, {
// 主进程特殊配置
}),
preload: mergeConfig(commonConfig, {
// 预加载脚本特殊配置
}),
renderer: mergeConfig(commonConfig, {
resolve: {
alias: {
'@': path.resolve('src/renderer/src')
}
}
})
})
4. UI集成与开发优化
4.1 Element Plus深度整合
安装UI库时,推荐使用自动导入方案以减小打包体积:
bash复制pnpm add element-plus
pnpm add -D unplugin-vue-components unplugin-auto-import
在main.ts中只需引入样式:
typescript复制import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css' // 暗黑模式
组件使用示例:
vue复制<template>
<el-button type="primary" @click="showMessage">测试</el-button>
</template>
<script setup lang="ts">
const showMessage = () => {
ElMessage.success('自动导入生效!')
}
</script>
4.2 开发体验优化
- 热更新配置:
在electron.vite.config.ts中添加:
typescript复制renderer: {
server: {
watch: {
usePolling: true // 解决部分系统文件监听失效问题
}
}
}
- 主进程调试:
在VSCode中配置launch.json:
json复制{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron-vite",
"runtimeArgs": ["dev"],
"outputCapture": "std"
}
]
}
5. 通信机制与安全实践
5.1 IPC通信封装
安全通信的最佳实践是通过预加载脚本暴露有限API。这是我的实现方案:
src/preload/index.ts:
typescript复制import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel: string, ...args: any[]) => {
const validChannels = ['window-minimize', 'window-close']
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, ...args)
}
},
receive: (channel: string, listener: (...args: any[]) => void) => {
const validChannels = ['update-available']
if (validChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) => listener(...args))
}
}
})
src/main/ipc.ts:
typescript复制import { ipcMain, BrowserWindow } from 'electron'
export function setupIPC(win: BrowserWindow) {
ipcMain.on('window-minimize', () => {
win.minimize()
})
ipcMain.on('window-close', () => {
win.close()
})
}
5.2 进程隔离实践
在渲染进程中使用封装好的API:
typescript复制declare global {
interface Window {
electronAPI: {
send: (channel: string, ...args: any[]) => void
receive: (channel: string, listener: (...args: any[]) => void) => void
}
}
}
// 调用示例
window.electronAPI.send('window-minimize')
window.electronAPI.receive('update-available', (version) => {
console.log('新版本:', version)
})
6. 打包与分发策略
6.1 多平台构建配置
修改package.json中的构建配置:
json复制{
"build": {
"appId": "com.example.myapp",
"productName": "My Electron App",
"copyright": "Copyright © 2023",
"mac": {
"category": "public.app-category.productivity",
"target": ["dmg", "zip"]
},
"win": {
"target": ["nsis", "portable"]
},
"linux": {
"target": ["AppImage", "deb"]
},
"files": ["out/**/*"],
"directories": {
"output": "release"
}
}
}
6.2 构建优化技巧
- 依赖排除:
在electron.vite.config.ts中配置外部依赖:
typescript复制main: {
build: {
rollupOptions: {
external: ['electron-updater']
}
}
}
- 资源压缩:
安装压缩插件:
bash复制pnpm add -D vite-plugin-compression
配置使用:
typescript复制import viteCompression from 'vite-plugin-compression'
// 在renderer插件中添加
viteCompression({
algorithm: 'gzip',
ext: '.gz'
})
7. 常见问题与解决方案
7.1 打包后白屏问题
现象:生产环境加载空白页面
解决方案:
- 确保路由使用hash模式
- 修改主进程加载逻辑:
typescript复制win.loadFile(path.join(__dirname, '../renderer/index.html'))
7.2 静态资源加载
正确姿势:
typescript复制// 开发环境使用绝对路径
const logoPath = import.meta.env.DEV
? '/src/assets/logo.png'
: path.join(__dirname, '../../src/assets/logo.png')
7.3 原生模块集成
以集成sqlite3为例:
- 安装依赖:
bash复制pnpm add sqlite3
pnpm add -D @types/sqlite3
- 配置外部依赖:
typescript复制main: {
build: {
rollupOptions: {
external: ['sqlite3']
}
}
}
- 使用require加载:
typescript复制const sqlite3 = require('sqlite3').verbose()
8. 进阶优化方向
8.1 自动更新实现
- 安装依赖:
bash复制pnpm add electron-updater
- 主进程配置:
typescript复制import { autoUpdater } from 'electron-updater'
autoUpdater.setFeedURL({
provider: 'generic',
url: 'https://your-update-server.com/'
})
autoUpdater.on('update-available', () => {
mainWindow.webContents.send('update-available')
})
8.2 性能监控
集成性能监控工具:
typescript复制import perfHooks from 'perf_hooks'
const start = perfHooks.performance.now()
// ...执行操作
const duration = perfHooks.performance.now() - start
console.log(`操作耗时:${duration.toFixed(2)}ms`)
8.3 安全加固
- 启用沙箱:
typescript复制new BrowserWindow({
webPreferences: {
sandbox: true
}
})
- CSP设置:
html复制<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
经过多个项目的实践验证,这套技术栈在开发效率、运行性能和跨平台兼容性方面表现出色。特别是在处理复杂业务逻辑时,TypeScript的类型系统能有效减少运行时错误。建议在大型项目中配合Pinia进行状态管理,可以更好地组织代码结构。