create-electron-vite 四个 build 坑与统一修复
2026年9月27日 · 技术
姊妹篇:《create-electron-vite 上手:从建项目到 AI 编程》——项目怎么建、AI 怎么接,看那篇;这篇专治 build。
先说结论:create-electron-vite 的四个 build 问题,全部与框架选择无关——React、Vue、Vanilla 三个变体一个都躲不掉,因为它们同源:这个脚手架本质上是「Vite 官方 Web 模板 + 一层很薄的 Electron 补丁」,补丁自己还带病。好消息是,四个问题存在一套对所有变体统一的修复动作,文末有可以直接粘给 AI 的清单。
四个问题一句话预览:
- TS6133:模板自带的
require声明没人用,首次 build 必红; - electron 下载失败:两套下载时机 + 一套只认平铺文件的缓存互不相通,GitHub 直连不稳就死在打包段;
- asar 体积虚胖:
dependencies的整棵 node_modules 被塞进安装包,实测 282.8 MB → 34.7 MB; - 幽灵文件:
dist-electron/没人清理,旧构建产物永久残留并进包。
验证方式:React 与 Vue 两个变体分别用这个脚手架新建并完整构建,另有一个 React 系真实项目(electron 35.7.5)做对照;文中每个修复都在真实构建里验证过。环境:Windows 11、Node v24.15.0、create-electron-vite 0.7.1、electron-builder ^24.13.3。
理解 npm run build 实际执行的三段,后面的「本质」才好讲:
tsc(或 vue-tsc) → vite build → electron-builder
类型检查 前端打包 桌面安装包打包
产出 dist/(渲染层) 引用 dist-electron/(主进程)
+ release/ 下的安装包
electron-builder 干的事:把 dist/、dist-electron/、package.json、以及 package.json 里 dependencies 的整个 node_modules 生产树,连同 electron 运行时本体,组装成 app.asar 和安装包。记住「dependencies 会被整个塞进包里」这句,后面要用。
打个比方,发布一个桌面应用就是搬一次家:
tsc是验房师,拿着图纸逐项挑毛病;vite build是装修队,把毛坯房(源码)装成精装房(dist/),家具直接焊死在墙上;electron-builder是搬家公司,把整套房连同车钥匙(electron 运行时)一起装进集装箱(安装包)。接下来的四个问题,全出在搬家的路上。
问题 1:新建完第一次 build 就报 TS6133(React 也不幸免)
现象:npm run build 第一步就死,React 变体实测日志:
> tsc && vite build && electron-builder
electron/main.ts(6,7): error TS6133: 'require' is declared but its value is never read.
本质:模板自带的 electron/main.ts 里有一行 const require = createRequire(import.meta.url),从头到尾没被用过;而脚手架会给所有变体的 tsconfig include 补上 "electron"(源码里无条件执行),且模板开着 noUnusedLocals。「未使用 + 强制检查」相撞,三种模板开箱即红。我们用 React 变体验证过:electron/ 目录的类型检查是真实生效的(在 main.ts 里埋一个类型错误,tsc 立刻报 TS2322)——所以别想着把 electron 从 include 里摘掉来绕,那等于放弃主进程的类型保护。
还是搬家的说法:装修队交房时在墙里留了根废线头,而验房师的规矩偏偏是「见到没用的东西就拒收」。房子结构没问题,是遗留物和严格的标准在相撞——所以修法不是放宽标准,而是拔掉那根线头。
修复:删掉 electron/main.ts 顶部这两行(对所有变体都这么做):
- import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
...
- const require = createRequire(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
问题 2:build 卡在「下载 electron」,报 ERR_ELECTRON_BUILDER_CANNOT_EXECUTE
现象:前两段都过了,electron-builder 阶段报:
⨯ Get "https://github.com/electron/electron/releases/download/v30.5.1/electron-v30.5.1-win32-x64.zip": ...
⨯ app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE
本质:Electron 二进制有两套下载时机和一套真正的缓存,它们互不相通。
npm install时,electron 包的安装脚本把运行时解压到node_modules/electron/dist——这一步很多人以为是「下载好了」;- 打包时,electron-builder 不碰
node_modules/electron/dist,它按package.json里的 electron 版本号,去找一个 zip 文件:%LOCALAPPDATA%\electron\Cache\electron-v<版本>-win32-x64.zip(实测 24.13.3 只认这个目录下的平铺同名文件,不递归子目录); - 找不到 → 当场从 GitHub Releases 下载。GitHub 直连不稳(超时 / EOF),build 就死在这里。
继续搬家:装修队已经把一套同款大理石台面搬进了你家(
node_modules/electron/dist),但搬家公司不进你家验收,只按型号去仓库提「原厂原包装」的货;而仓库管理员找货只认货架上贴着标准标签的那一层,塞在无名纸箱里的同款他视而不见。「家里明明有」和「仓库提得出」是两本账。
两个最容易踩的认知坑:
- 「npm install 都成功了,怎么打包还要下载?」——因为两步用的是两份东西。npm install 的下载(新版 @electron/get)会把 zip 存进
%LOCALAPPDATA%\electron\Cache\<sha256 哈希目录>\里;而 electron-builder 只认平铺文件名,哈希子目录里的 zip 它看不见。这就是「我明明有缓存还是失败」的原因。 - 镜像下载完会不会存下来? 会。配了镜像后第一次下载,zip 会落到上面那个平铺位置,之后的 build 离线也能过(实测确认)。
修复(推荐,写进项目配置一劳永逸)——electron-builder.json5:
{
// GitHub 直连不稳,二进制走 npmmirror
"electronDownload": {
"mirror": "https://npmmirror.com/mirrors/electron/"
},
"directories": { "output": "release/${version}" },
// ...
}
离线兜底:手动把 zip 按 electron-v<版本>-win32-x64.zip 的名字平铺放进 %LOCALAPPDATA%\electron\Cache\(比如从哈希子目录里拷出来),下次 build 直接命中。
问题 3:安装包体积离谱——asar 里塞了整个 node_modules
现象:一个几乎没写代码的项目,asar 有几 MB 到十几 MB;真实项目能到几百 MB。
本质:dependencies 的真实含义不是「项目用到的包」,而是「electron-builder 会打进 asar 的包」。而 Vite 打包渲染层时,已经把 React/Vue 和所有前端库编译进了 dist/——node_modules 里那些原件是第二份,纯死重。
dependencies不是你的购物清单,而是搬家公司的发货清单——装修队明明已经把家具焊死在精装房里了,搬家公司仍按清单把仓库里的原材料原样再发一份,全塞进你的集装箱。
实测(前 = 模板原样,后 = 依赖分区调整后):
| 项目 | asar 总体积 | 其中 node_modules |
|---|---|---|
| React 模板新项目 | 4.9 MB | 4.7 MB(react 树 5 个包) |
| React 模板新项目调整后 | 0.1 MB | 0(node_modules 整个消失) |
| Vue 模板新项目 | 14.1 MB | 14.0 MB(vue 全家桶 12 个包) |
| Vue 模板新项目调整后 | 0.1 MB | 0 |
| 真实项目(React 系,含图表库) | 282.8 MB | 269.2 MB(mermaid 一家 122MB) |
| 真实项目调整后 | 34.7 MB | 23.6 MB |
安装包同步瘦身:139 MB → 91.7 MB。
修复:package.json 里做依赖分区——
- 留在
dependencies:主进程运行时真正需要的第三方包。判断方法:electron/main.ts(及主进程其他文件)import 的包;如果配置了vite.config.ts的external清单,就以它为准; - 移到
devDependencies:一切渲染进程专用的库(react / react-dom / vue 等框架本体、recharts 等图表库、lucide 等图标库、UI 组件库)。devDependencies 照常安装、Vite 照常打包,只是不进 asar。
一条判断口诀:「删掉这个包,打包出来的应用还能不能跑?」能跑 → devDependencies。
问题 4:asar 里混进旧构建的「幽灵文件」
现象:dist-electron/ 里出现没被任何代码引用的 .js 文件(带内容哈希的旧 chunk),并且被原样打进安装包。真实项目里曾积到 7 个文件、约 2 MB,其中包括两份 950 KB 的完整旧版主进程 bundle。
本质:vite build 只清理自己的输出目录 dist/;dist-electron/ 是 vite-plugin-electron 写的,没人清。只要某次改动让输出文件名变化(重命名模块、动态 import 增删、chunk 拆分),旧文件就永久残留。React 变体实测复现:主进程加一个动态 import 模块 extra.ts(产出 extra-DPF_M5d5.js),把它改名 extra2.ts 再 build——旧的 extra-DPF_M5d5.js 仍然躺在 dist-electron/ 里并被打进 asar。
这套房里只有一间屋有人打扫(
dist/,装修队每次自己清);dist-electron/那间换了家具也没人扔旧的,而搬家公司打包时是「整间房连灰尘一起装箱」——旧东西越多,集装箱越沉。
修复:build 脚本前置清理(package.json):
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('dist-electron',{recursive:true,force:true})\" && tsc && vite build && electron-builder"
顺手要修的小问题
.gitignore缺口(脚手架的 bug):脚手架其实带了往.gitignore补dist-electron和release的逻辑,但因为它用「整行精确匹配」找插入点,而模板文件是 CRLF 行尾,匹配永远失败——补丁静默失效,所有变体的.gitignore都缺这两行,构建产物会被提交进 git。手动补上即可;- 占位符元信息:
electron-builder.json5里appId: "YourAppID"、productName: "YourAppName"是脚手架的占位符,直接决定安装包文件名(实测产物叫YourAppName-Windows-0.0.0-Setup.exe)和安装目录,改成真实值; - 元数据警告:
description is missed / author is missed——在package.json补这两个字段即可,不影响构建; - 图标:
default Electron icon is used——把build/icon.png(≥256×256)放进项目根的build/目录,electron-builder 会自动识别。
统一修复清单(复制给 AI 一次做完)
以上修复对三种模板完全一致,可以整段粘给 AI 一次执行(也可以自己动手,10 分钟的事):
这个项目由 create-electron-vite 0.7.1 生成,按顺序完成以下修复,每步做完跑 npx tsc --noEmit 验证:
1. 删除 electron/main.ts 中未使用的 require 声明:
"import { createRequire } from 'node:module'" 和 "const require = createRequire(import.meta.url)" 两行
2. 依赖分区:渲染进程专用依赖(react、react-dom,Vue 项目则是 vue)从 dependencies
移到 devDependencies;dependencies 只留主进程运行时需要的包
3. build 脚本最前面加产物清理:
node -e "require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('dist-electron',{recursive:true,force:true})" &&
4. electron-builder.json5 增加:
"electronDownload": { "mirror": "https://npmmirror.com/mirrors/electron/" }
5. .gitignore 补两行:dist-electron/ 和 release/(脚手架的自动补丁因 CRLF 静默失效了)
6. electron-builder.json5 里 YourAppID / YourAppName 占位符改成真实应用名
全部完成后跑一次 npm run build 确认通过,报告 asar 体积。
修复后怎么验收
npm run build全程无下载日志(或有 npmmirror 下载且成功);ls dist-electron/文件数符合预期(模板项目就是main.js+preload.mjs+ 你自己的 chunk),没有名字眼生的孤儿文件;- asar 体积合理:模板级项目 < 1 MB,纯模板甚至没有
node_modules目录; - 冒烟:直接运行
release/<版本>/win-unpacked/<应用名>.exe,窗口能起来即主进程依赖齐全。
如果你在 Electron 构建上被卡住的不止这四个问题,或者想让人直接帮你把项目捋顺——欢迎聊聊。