在 vue3 + vite + electron 项目里配 HMR,第一步不是安装插件,而是先认清你改的代码属于哪一层。很多人发现主进程改动后没有热更新,以为是配置坏了,其实是从一开始就搞错了热更新的范围和边界。
先别急着改配置,分清楚 HMR 的生效层
在 electron 项目中,需要先判断你要热更新的目标。渲染进程对应浏览器窗口内的页面,直接由 Vite 服务提供,天然支持 HMR。而主进程负责创建窗口和管理生命周期,Vite 默认不会对它做热更新,任何主进程代码修改都需要重启应用才能生效。所以配置 HMR 前,先明确你的改动落在哪一层,否则会误以为配置失败。
如果只改渲染进程的组件代码,比如 Vue 文件里的模板、样式或脚本,Vite 默认的 HMR 就会工作,不需要额外配置。如果改的是主进程入口、窗口参数、IPC 处理逻辑,那 HMR 一般不适用,只能通过自动重启来加速开发。这里的“自动重启”通常由插件或工具完成,效果上类似于热更新,但底层机制完全不同,需要你自己辨别。
用 vite-plugin-electron 把主进程纳入 Vite 构建
建议在 vite.config.ts 中安装并使用 vite-plugin-electron,它会将主进程代码作为 Vite 的另一个构建入口,并监听主进程文件变化,变化后自动重启 electron 并重新构建。对于渲染进程,确保 dev 模式下 electron 加载的是 Vite 开发服务器地址,比如 http://localhost:5173,而不是打包后的文件。
安装依赖时,把它放到 devDependencies 即可:
npm i -D vite-plugin-electron
然后在 vite.config.ts 里给它单独配置主进程入口,示例:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import electron from 'vite-plugin-electron'
export default defineConfig({
plugins: [
vue(),
electron({
main: {
entry: 'electron/main.ts',
},
}),
],
})
这里只列出了 main 入口。preload 脚本如果有独立路径,也可以单独加一个 preload 键,具体看插件版本和项目结构,但先保证主进程能构建成功再扩展。
绑定固定端口,避免白屏和失联
一个常见的坑是 Vite 的开发服务器端口被占用或与 electron 的默认端口冲突。此时 Vite 会自动换端口,但 electron 中配置的加载地址还是旧端口,导致白屏且无 HMR。建议显式指定 server.port 并在 electron 加载时用同一常量。另一个坑是文件监听器可能因为 IDE 保存方式或系统限制,漏掉一些文件变化,导致 HMR 不触发,这时可以调整 chokidar 的 watchOptions,比如增加 usePolling。
所以,在 vite.config.ts 里建议这样设置:
export default defineConfig({
server: {
port: 5173,
strictPort: true,
},
})
strictPort 可以阻止 Vite 在端口被占用时自动切换,宁可报错也不要悄悄换端口。同时把“http://localhost:5173”这个地址抽到全局常量,供 electron 主进程和 vite 配置共用。比如在项目根目录建一个 dev-constants.ts,导出 DEV_SERVER_URL,然后在 main.ts 里使用。
确认生效看这几个信号
配置完成后,不要只看终端有没有报错,直接改代码观察现象。先改渲染进程,比如修改某个组件的文字颜色,如果页面没有刷新就自动更新,说明渲染进程 HMR 正常。再改主进程,比如调整 BrowserWindow 的宽高,这时 electron 会自动退出并重新启动新实例,新窗口应应用新尺寸。注意这里主进程的重启是模拟出来的“热更新”,不是 Vite 的 HMR,但对开发效率的影响是等效的。
如果你的改动发生在 preload 脚本,很多配置里它不会自动重启,需要手动重新编译。判断方式可以看终端日志,vite-plugin-electron 一般会输出 re-compliling 或 restarting 等字样。如果什么都没有,检查文件路径是否正确纳入了监听范围。
生产环境要切回打包文件,别把 HMR 带进发布流程
HMR 本身只在开发模式有意义。生产环境的 electron 应该加载构建后的静态资源,比如 dist 目录,而不是 localhost 地址。所以 main.ts 里要写清楚环境判断,生产环境用 loadFile,开发环境用 loadURL。另外,如果渲染进程里引用了外部资源或第三方 API,Electron 的 webSecurity 和页面 CSP 可能会拦截 Vite 的 WebSocket 信号,导致页面不刷新。这类问题容易被误判成 HMR 失效,需要先排除安全策略的影响。
还有一层干扰来自开发者工具。DevTools 打开时,某些插件或调试器可能屏蔽前端的模块热替换通知,让页面停留在旧状态。建议遇到“改完没反应”时,先关闭 DevTools 再试一次,或者直接看网络面板里有没有到 ws://localhost:5173 的连接。