Vue3 + Electron + Vite 从0到1搭建桌面客户端全攻略
最近在帮一个朋友搭桌面客户端技术栈选了Vue3 Electron Vite整个过程踩了不少坑也梳理出了一套相对顺手的搭建流程。所以这期就准备把“从0到1搭建项目”的第一期完整记录下来怎么初始化工程、怎么把Vite的dev server和Electron串起来、主进程和渲染进程怎么分工、打包之前要做什么准备。适合刚准备入坑Electron、或者从纯Web开发转客户端开发的同学参考已经用过Electron的老手也可以看看我在目录结构和调试链路上的处理方式有些细节是文档里不会直接告诉你的。1. 为什么是Vue3 Electron Vite这个组合1.1 这个组合到底解决了什么问题先说清楚这个技术栈存在的意义。Electron负责的是“桌面应用外壳”让网页代码跑在Chromium里同时提供访问文件系统、系统菜单、托盘、窗口管理等原生能力。Vue3负责的是UI层的组件化开发响应式、组合式API、生态成熟写复杂交互界面效率很高。Vite则是构建工具开发环境下用原生ESM做冷启动和热更新几秒钟就能起一个dev server比早期Webpack方案的编译等待体验好太多。这三者各管一摊组合在一起的好处是你仍然用Web技术写界面但交付的是一个可双击安装的桌面软件。对团队来说前端技能可以直接复用对产品来说跨平台Windows/macOS/Linux一套代码对开发者来说Vite带来的开发反馈速度几乎和纯前端项目一样快。1.2 和Tauri、Qt、Electron全家桶的对比选型的时候肯定绕不开对比。Tauri是Rust Web前端打包体积小、内存占用低但前提是你愿意碰Rust而且它调系统能力时需要通过Rust的命令接口对于纯前端团队有学习成本。Qt则是C的老牌方案性能和原生能力最强但界面用QML或Widgets开发和前端生态基本脱节招人也是个问题除非你们团队本身就是桌面端出身否则不推荐。Electron最直接的槽点是体积大——默认打包出来一个安装包动辄100MB往上内存占用也比原生应用高。但对绝大多数业务型客户端聊天工具、编辑器、后台管理、内部工具来说这些代价换来的是Vue/React生态的完整复用、调试链路短、社区资料多性价比非常高。1.3 适合什么场景不适合什么场景结合我的实际经验这套组合适合跨平台业务应用、需要频繁迭代的桌面端、团队主要是Web前端、需要复用现有前端组件库的项目。不适合的场景也有比如对安装包体积极其敏感、需要极低内存占用的工具类应用或者对硬件外设、系统底层性能要求极高的软件。这时候再去考虑Tauri或原生方案更合理。提示选型不是越新越好而是看团队能维护什么。Electron的生态稳定度和踩坑资料丰富度在2025年的今天仍然是最好的。2. 环境准备与项目初始化2.1 前置条件Node版本和包管理器启动之前先把环境理顺。Electron对Node版本没有严格绑定它是内置自己的Node运行时但Vite和electron-builder对Node版本有要求。我这边用的是Node 20长期支持版npm 10以上。建议你至少用Node 18太老的版本会遇到Vite启动报错或build阶段崩溃的问题。如果你同时装了多个Node版本推荐用版本管理工具这样切项目不会互相污染。另外包管理器我统一用npm因为electron-builder和Vite对npm的兼容性最稳团队协作时也少一些不确定因素。2.2 用Vite脚手架创建Vue3项目初始化Vue3项目很简单直接在命令行执行npm create vitelatest my-desktop-app -- --template vue这里我用了vue模板它默认生成的是适合纯Web项目的结构后续我们会手动调整。执行完进入项目目录cd my-desktop-app npm install装完依赖先跑一下npm run dev确认Web版本的Vue项目正常启动。这一步是为了隔离问题——如果纯Vite项目都起不来那肯定不是Electron的问题先把基础环境搞定再往下走。2.3 安装Electron及配套依赖接下来安装桌面端相关依赖这是整期堆栈的核心npm install electronlatest --save-dev npm install electron-builder concurrently cross-env --save-dev说一下这几个包各自的作用electron桌面运行环境安装的时候会下载预编译的二进制文件国内网络环境下如果下载慢可以配置electron镜像源加速具体方法是创建.npmrc文件写入镜像地址这是社区常用的公开加速方案和代理无关。electron-builder打包工具负责生成安装包NSIS、dmg等格式同时处理图标、签名、资源文件。concurrently用来并行启动两个进程让我们一条命令同时拉起Vite和Electron。cross-env跨平台设置环境变量的工具Windows上直接用NODE_ENVxxx会失败用它包装一下就行。装完之后跑npx electron --version能看到版本号说明Electron本体已经可以运行了。3. 目录结构与进程模型设计3.1 理解Electron的主进程与渲染进程进入编码之前必须先理解Electron的进程模型。Electron应用启动后会有两类进程主进程Main Process和渲染进程Renderer Process。主进程是Node环境负责创建窗口、管理应用生命周期、调用系统API渲染进程就是浏览器环境负责渲染Vue页面、跑前端业务逻辑。这两个进程之间通过IPC通信不能直接互相调用对方的变量。这种模型理解起来像是“浏览器和网页”的关系只不过浏览器被换成了Electron的窗口管理器网页被换成了我们的Vue应用。┌──────────────┐ IPC ┌──────────────┐ │ 主进程 │◄───────────►│ 渲染进程 │ │ Node环境 │ │ Chromium环境 │ │ 窗口/生命周期 │ │ Vue页面 │ └──────────────┘ └──────────────┘这段图画给团队新人看很直观。日常开发中90%的代码在渲染进程写只有窗口管理、系统交互才需要主进程出马。3.2 目录结构规划src、electron、public怎么分初始化之后的默认结构是按Web项目组织的我建议把它改造成桌面端友好的结构my-desktop-app/ ├── electron/ │ ├── main.cjs # 主进程入口 │ ├── preload.cjs # 预加载脚本 │ └── utils/ ├── src/ │ ├── main.js # Vue应用入口 │ ├── App.vue │ └── components/ # 页面组件 ├── public/ │ └── icon.png # 静态资源 ├── index.html ├── vite.config.js ├── package.json └── .npmrc把主进程相关文件单独放在electron目录目的很清楚不让它混进Vite的构建范围。Vite构建时只会处理src和index.htmlelectron目录下的代码由Electron自己加载互不干扰。3.3 主进程入口文件怎么写主进程入口是整个桌面应用的启动起点。在electron/main.cjs里创建一个基本的BrowserWindowconst { app, BrowserWindow } require(electron); const path require(path); function createWindow() { const win new BrowserWindow({ width: 1024, height: 768, webPreferences: { preload: path.join(__dirname, preload.cjs), contextIsolation: true, nodeIntegration: false } }); win.loadURL(process.env.VITE_DEV_SERVER_URL); } app.whenReady().then(() { createWindow(); app.on(activate, () { if (BrowserWindow.getAllWindows().length 0) createWindow(); }); }); app.on(window-all-closed, () { if (process.platform ! darwin) app.quit(); });这个文件里的几个关键配置后面还会细讲先记住loadURL加载的是Vite的dev server地址也就是说开发模式下Electron窗口里跑的就是热更新的Web页面。4. 打通Vite与Electron开发模式调试4.1 用concurrently实现一条命令同时启动开发模式最舒服的体验是执行一条命令Vite dev server和Electron窗口同时起来。这里需要改造package.json的scripts{ scripts: { dev:web: vite, dev:electron: wait-on http://localhost:5173 cross-env VITE_DEV_SERVER_URLhttp://localhost:5173 electron ., dev: concurrently -k \npm run dev:web\ \npm run dev:electron\ } }注意看这个配置里有一个核心细节dev:electron脚本开头用了wait-on意思是等Vite的端口起来之后再启动Electron否则Electron窗口打开时页面还是空白的。这个细节很多人第一次做的时候会漏掉导致窗口一闪而过或白屏查半天不知道怎么回事。-k参数表示如果其中一个进程挂掉另一个也一起结束避免后台残留一个孤儿进程占着端口。4.2 渲染进程加载策略dev server还是本地文件开发模式下用loadURL加载http://localhost:5173这样才能享受热更新。但打包后没有dev server必须改成加载本地HTML文件。这里的典型做法是先判断环境变量if (process.env.VITE_DEV_SERVER_URL) { win.loadURL(process.env.VITE_DEV_SERVER_URL); } else { win.loadFile(path.join(__dirname, ../dist/index.html)); }判断依据就是启动脚本里注入的环境变量。有它说明是开发模式没有则加载构建产物。第一次接触Electron的人很容易忽略base配置——Vite默认生成的资源路径是/开头打包后electron加载本地文件时报file协议路径错误画面空白。解决方法是把vite.config.js里的base设为./export default defineConfig({ plugins: [vue()], base: ./ });这样构建出来的HTML里引用的js/css就是相对路径file协议能正确解析这是我用血泪换来的教训大家直接抄作业就行。4.3 热更新与主进程重启的配合Vite的热更新只对渲染进程生效。也就是说你改了Vue组件窗口里的界面会实时刷新但如果你改了electron/main.cjsElectron窗口不会自动重启必须手动关掉重新跑一遍npm run dev。处理这个问题有两类方案一类是装electron-reload或nodemon监听electron目录文件变化后自动重启Electron进程另一类是干脆记住“改了主进程就要重启”这个规则开发初期先忍一忍等工程改到足够复杂再上自动重启工具。我个人的建议是先手动原因很简单第一期项目里主进程改动频率很低引入额外的监听工具会增加变量等第二期再优化调试链路也不迟。5. 安全配置与浏览器窗口细节5.1 BrowserWindow关键参数解析BrowserWindow的每一项配置都值得花时间理解。width和height只是初始尺寸用户运行时可以改窗口大小如果你希望限制最小尺寸可以加minWidth和minHeight。show: false配合ready-to-show事件可以有效避免窗口加载时白屏闪烁const win new BrowserWindow({ width: 1024, height: 768, show: false, webPreferences: { preload: path.join(__dirname, preload.cjs), contextIsolation: true, nodeIntegration: false } }); win.once(ready-to-show, () { win.show(); });窗口先隐藏等页面渲染完毕再显示视觉上就是“秒开”这个细节对用户体验影响很大。如果你是做内部工具还可以用autoHideMenuBar: true把默认菜单栏藏掉省得界面顶部多一个不协调的菜单条。5.2 preload脚本与contextIsolation这是Electron安全布局里最核心的一个概念我的建议是永远保持contextIsolation: true和nodeIntegration: false不要为了图方便打开Node集成。原因是安全风险。如果渲染进程能直接访问Node接口页面里任何一个XSS漏洞都可能升级为整个系统的代码执行漏洞。正确的做法是通过preload脚本暴露一个最小化的API给页面const { contextBridge, ipcRenderer } require(electron); contextBridge.exposeInMainWorld(desktop, { getVersion: () ipcRenderer.invoke(app:get-version) });然后渲染进程里通过window.desktop.getVersion()调用看到的就是一个干净的接口。这套Bridge机制既安全又清晰页面不知道自己跑在Electron里只知道自己有一个叫desktop的外部API可以用。5.3 常见安全坑nodeIntegration什么时候开网上很多老教程会教你把nodeIntegration设为true然后在Vue里用require这在早期的Electron是常见做法但现在已经是被安全社区反复批判的反模式。唯一的特例是本地离线工具、完全不加载远程内容、不处理外部输入的内部工具可以适当放宽。但即使如此我更建议用preload方案替代因为你不知道项目后续会不会加第三方脚本或iframe一旦加了风险就出现了。注意不要因为“麻烦”就关闭contextIsolation。这个开关一旦关掉之后想再打开就得把所有渲染进程里的Node调用全部挪回preload改造成本是成倍增加的。6. 打包与发布前的准备6.1 electron-builder配置打包是本期的最后一段electron-builder的配置项非常多第一期我们只需要做一个能跑的安装包。配置文件可以放在package.json的build字段里也可以单独建electron-builder.yml文件。我推荐单独建文件配置一多起来JSON格式写着很难受。appId: com.example.desktop productName: MyDesktopApp directories: output: release files: - dist/** - electron/** win: target: nsis mac: target: dmg nsis: oneClick: false allowToChangeInstallationDirectory: true打包前要做两件事一是执行npm run build生成dist目录这是Vite打包出来的渲染进程资源二是确认electron目录里的代码是否需要编译。我们用的是cjs格式Electron原生支持不需要额外转译这也是为什么约定electron目录下的文件用cjs而不用esm省掉一层构建配置。6.2 应用图标、名称、版本号设置产品名称、版本号、图标这些元信息直接影响安装包长什么样。产品名在yaml里配置版本号沿用package.json的version字段图标则有格式要求——Windows上用.icomacOS上用.icns如果暂时没有图标文件electron-builder会使用默认的Electron图标兜底。我建议第一期先别花太多时间设计图标用默认图标走通全流程。等部署的时候再补图标因为图标格式转换本身就要额外处理别让图标问题阻塞验证链路。6.3 首次打包的注意事项打包命令很简单npm run build npx electron-builder --win但首次打包通常会遇到几个问题。一个是网络问题electron-builder需要下载构建工具比如NSIS、winCodeSign等国内网速会很慢配置镜像或重试都可以解决。另一个是打包目录权限问题Windows下如果杀毒软件拦截了下载的二进制文件打包会报错需要把相关目录加入信任区。打包完成后在release目录下能看到setup安装包和未压缩的win-unpacked目录。如果你只是想快速验证效果直接运行win-unpacked里的exe即可不用每次打完整安装包。7. 踩坑实录第一期常遇问题速查7.1 环境类问题这里是按真实的报错现场整理的速查表遇到同级问题先对照这里排查报错现象常见原因解决办法npm create vite卡在依赖安装网络波动或源不稳定清缓存重试检查.npmrc的源配置Electron did not start correctlyElectron启动时缺少运行参数或被杀软拦截用命令行直接启动electron目录下的exe看详细报错Node options requires系列错误Node版本过旧升级到Node 18打包报ERR_ELECTRON_BUILDER_CANNOT_EXECUTE缺少构建组件或路径含中文确认项目路径中不要包含空格和中文7.2 配置类问题Vite build后页面白屏90%是没设base: ./。dev模式下Electron窗口白屏大概率是wait-on没等Vite起来检查dev脚本。主进程改了没反应Electron不会自动重启主进程需要手动重新执行dev命令。preload里不能使用ESM语法Electron对cjs预加载支持最稳preload文件统一用.cjs后缀。7.3 运行时问题还有一个很容易被忽略的坑渲染进程请求接口时会遇到跨域问题。开发模式下Vue跑在localhost:5173后端接口跑在另一个端口浏览器会阻止跨域请求。解决思路是在vite.config.js里配置server.proxy把/api开头的请求转发到目标端口生产环境下再让后端解决跨域或仍用代理方式处理。这一点值得单独强调因为很多第一次做Electron的朋友以为Electron没有跨域限制结果白屏了半天。Electron的渲染进程本质上还是ChromiumWeb的安全策略它都会遵守别拿例外当默认。最后分享一点实际使用体会这套Vue3 Electron Vite的组合我实际跑了几个项目之后最大的感受是“开发体验被Vite拉高了但复杂度没有被Electron拉垮”。只要守住几根红线——目录清晰、主进程保持轻量、preload做安全隔离、dev脚本用wait-on后面迭代就非常顺。第一期的目标是把地基打正不用急着接过多插件和自动化工具先跑通“开发-构建-打包”这条主链路。下期我会接着讲主进程和渲染进程的IPC通信、托盘与多窗口管理以及如何把调试体验再优化一档。如果你在实际搭建中卡在哪一步可以按这期的问题表先自查大概率是base配置或wait-on时序的问题。