☰
Vue3 + Electron + Vite 从0到1搭建桌面客户端全攻略
2026/10/9 3:26:04 网站建设 项目流程

最近在帮一个朋友搭桌面客户端,技术栈选了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 vite@latest 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 electron@latest --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_ENV=xxx会失败,用它包装一下就行。

装完之后,跑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.html,electron目录下的代码由Electron自己加载,互不干扰。

3.3 主进程入口文件怎么写

主进程入口是整个桌面应用的启动起点。在electron/main.cjs里创建一个基本的BrowserWindow:

const { 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_URL=http://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.cjs,Electron窗口不会自动重启,必须手动关掉重新跑一遍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上用.ico,macOS上用.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的渲染进程本质上还是Chromium,Web的安全策略它都会遵守,别拿例外当默认。

最后分享一点实际使用体会

这套Vue3 + Electron + Vite的组合我实际跑了几个项目之后,最大的感受是“开发体验被Vite拉高了,但复杂度没有被Electron拉垮”。只要守住几根红线——目录清晰、主进程保持轻量、preload做安全隔离、dev脚本用wait-on,后面迭代就非常顺。第一期的目标是把地基打正,不用急着接过多插件和自动化工具,先跑通“开发-构建-打包”这条主链路。下期我会接着讲主进程和渲染进程的IPC通信、托盘与多窗口管理,以及如何把调试体验再优化一档。如果你在实际搭建中卡在哪一步,可以按这期的问题表先自查,大概率是base配置或wait-on时序的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询