Electron 的西天取经
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<blockquote>
<p>我本身是做 Web 开发的,最近需要写个小工具,调用一些系统 API,就选择了比较成熟的 Electron。结果业务代码写了三小时,环境配置、镜像、文件引用、打包路径、体积过大、文件被锁定……各种问题却折腾了将近三天。写下这篇笔记,记录这一路的坎坷。</p>
</blockquote>
<h3 data-id="heading-1">第一难:官方文档搭不起项目</h3>
<p>按照官方说明初始化项目:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">npm init
npm install electron --save-dev</pre>
</div>
<div>
<div>
<p>结果第一步就卡住了——<strong>装不上</strong>。</p>
<p>初步猜测是网络问题。虽然开梯子能解决,但不想一直挂着。查阅文档后发现,Electron 在国内需要配置镜像。</p>
<p>于是动手配置:</p>
<ol>
<li>找到 <code>.npmrc</code> 文件,路径可通过 <code>npm config list</code> 查看 <code>user</code> 字段;</li>
<li>加入镜像地址:<code>electron_mirror=https://npmmirror.com/mirrors/electron/</code></li>
</ol>
<p><strong>小结</strong></p>
<p>国内开发环境配置镜像算是常规操作,只是初次接触时容易让人心烦。</p>
<h3 data-id="heading-2">第二难:引入 Vue 与 Vite</h3>
<p>官方示例虽然能运行,但没有热更新,开发效率低。既然选择了 Electron,自然要利用前端生态,于是决定引入 Vue 和 Vite,配合 <code>vite-plugin-electron</code> 替换原有结构。</p>
<p>思维方式需要转变:原本是加载本地 HTML 文件,现在则要加载 Vite 启动的本地服务:<code>win.loadURL('http://localhost:3000')</code>。</p>
<p>另一个问题是开发环境与生产环境的差异,需要引入环境变量。最终使用 <code>cross-env</code> 实现:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">"dev": "cross-env NODE_ENV=development vite dev",</pre>
</div>
</div>
<div>
<div>
<p><strong>小结</strong></p>
<p>逻辑上并不复杂,关键在于理解 Electron 分为主进程与渲染进程,最终前端跑的还是编译后的 HTML 与 JS。理清整个流程后,内部实现就可以灵活替换。</p>
<h3 data-id="heading-3">第三难:如何调试?</h3>
<p>页面跑起来了,但怎么打开开发者工具?后端代码如何打断点?</p>
<p>首先在创建 <code>BrowserWindow</code> 时加上:</p>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">win.webContents.openDevTools({ mode: 'detach' })</pre>
</div>
<p>这样启动时就会自动打开调试工具。如果不小心关掉了,还可以用 <code>Ctrl + Shift + I</code> 重新打开。</p>
<p>主进程调试需要在 VSCode 中配置 <code>launch.json</code>,虽然不复杂,但配置起来略繁琐:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Electron with Vite",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"windows": {
"runtimeExecutable": "npm.cmd"
},
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"skipFiles": ["<node_internals>/**"],
"env": {
"NODE_ENV": "development",
"ELECTRON_IS_DEV": "true"
}
},
{
"name": "Attach to Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src/renderer",
"sourceMaps": true,
"sourceMapPathOverrides": {
"/@fs/*": "${webRoot}/*",
"/@id/*": "${webRoot}/*",
"/src/*": "${webRoot}/*",
"/node_modules/.vite/*": "${webRoot}/node_modules/*"
}
}
],
"compounds": [
{
"name": "Debug Main + Renderer",
"configurations": ["Debug Electron with Vite", "Attach to Renderer"]
}
]
}</pre>
</div>
<div>
<div>
<p>同时 Vite 配置也要配合调整端口与调试参数。</p>
<p><strong>小结</strong></p>
<p>前端调试工具的打开方式藏在文档深处,最后还是靠博客文章才找到;主进程调试则要配置 VSCode 和 Vite 插件,整套流程配下来,比写业务逻辑还耗时。</p>
<h3 data-id="heading-4">第四难:electron-forge 打包初体验</h3>
<p>一开始使用的是官方推荐的 <code>electron-forge</code>,但体验并不理想:</p>
<ol>
<li>直接生成 exe,没有安装引导,也没有卸载入口;</li>
<li>双击直接运行,还会附带一个无说明的更新进程;</li>
<li>配置引导进程不生效;</li>
<li>内存占用高达 600MB,明显是把 node_modules 全打包了;</li>
<li>中文文件名在安装后变成乱码;</li>
<li>打包时 asar 文件被占用,必须杀掉所有 Electron 进程才能继续。</li>
</ol>
<p><strong>小结</strong></p>
<p>或许是我没配置对,但整体使用体验确实不佳。</p>
<h3 data-id="heading-5">第五难:electron-builder 打包卡在下载</h3>
<p>于是换用 <code>electron-builder</code>,结果打包时卡在三个依赖包的下载:</p>
<ul>
<li>winCodeSign-2.6.0</li>
<li>nsis-3.0.4.1</li>
<li>nsis-resources-3.4.1</li>
</ul>
<p>又是网络问题。有两种解决办法:</p>
<p><strong>手动下载并放置缓存</strong></p>
<p>将下载的包解压到以下路径:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">C:\Users\15034\AppData\Local\electron-builder\Cache\winCodeSign\winCodeSign-2.6.0
C:\Users\15034\AppData\Local\electron-builder\Cache\nsis\nsis-3.0.4.1
C:\Users\15034\AppData\Local\electron-builder\Cache\nsis\nsis-resources-3.4.1\plugins</pre>
</div>
</div>
<p>配置镜像</p>
<p>在 <code>.npmrc</code> 中加入:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/</pre>
</div>
<div>
<div>
<h3 data-id="heading-6">第六难:打包流程未完成</h3>
<p>打包过程没有报错,但 release 文件夹没有生成,流程未完整执行。</p>
<p>排查后发现,主进程入口不是 <code>./dist/main/main.js</code>,而是由 Vite 插件控制的 <code>./dist-electron/main.js</code>。</p>
<p><strong>小结</strong></p>
<p>入口路径的理解偏差,让成功只差一步,却耗费不少时间。</p>
<h3 data-id="heading-7">第七难:打包时文件被占用</h3>
<p>又遇到文件占用错误:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">EBUSY: resource busy or locked, unlink '.../dist/win-unpacked/resources/app.asar'</pre>
</div>
</div>
<p>原因是 <code>vite build</code> 和 <code>electron-builder</code> 同时操作 <code>dist/</code> 目录,导致文件被锁定。</p>
<p>解决方案是让它们使用不同的输出目录。修改 Vite 配置:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">outDir: fileURLToPath(new URL('./renderer-dist', import.meta.url))</pre>
</div>
</div>
<div>
<p>同时调整 <code>electron-builder.config.js</code> 中的 <code>files</code> 配置:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">files: ['renderer-dist/**/*', ...]</pre>
</div>
<div class="code-block-extension-header">
<div>
<p>但打包后仍然没有生成 release 文件夹,日志显示输出在 <code>dist\electron-deploykit Setup 0.0.1.exe</code>,似乎配置文件未生效。</p>
<p>检查 <code>dist\builder-effective-config.yaml</code>,确认配置未加载。尝试修改配置文件名、加入日志打印、甚至回退版本,均无效。</p>
<p>最后将配置文件改为 <code>electron-builder.config.yml</code>,居然成功了。</p>
<h3 data-id="heading-8">第八难:包体积过大</h3>
<p>一个简单页面打包后居然占用了 700MB。</p>
<p>将 <code>asar</code> 设为 <code>false</code> 后,发现果然是 <code>node_modules</code> 被全部打包。于是在配置中将其排除:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">files:
- ./renderer-dist/**/*
- ./dist-electron/**/*
- ./package.json
- '!./**/*.map'
- '!node_modules/**/*'</pre>
</div>
<div>
<div>
<h3 data-id="heading-9">第九难:ESM 与 CommonJS 模块冲突</h3>
<p>打包安装后程序无法启动,日志显示模块引用错误。由于代码被压缩,难以定位问题。</p>
<p>取消压缩后,发现是 <code>const windowManager = require('/windowManager')</code> 这种 CommonJS 写法在 Vite 中不被支持。</p>
<p>Electron 官方示例使用 CommonJS,但 Vite 默认使用 ESM。最终将所有模块改为 ESM 写法。</p>
<p>也可尝试使用 <code>vite-plugin-commonjs</code> 插件转换,但我没有实际验证。</p>
<h3 data-id="heading-10">第十难:preload.js 未被打包</h3>
<p>开发环境运行时出现错误:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">Unable to load preload script: D:\code\DeployKit\electron-DeployKit\preload.js
Error: ENOENT: no such file or directory, open 'D:\code\DeployKit\electron-DeployKit\preload.js'</pre>
</div>
<p>检查发现 <code>dist-electron</code> 下没有 <code>preload.js</code>。</p>
<p>于是在 Vite 配置中加入自定义插件,手动复制 preload 文件:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function copyPreloadPlugin() {
return {
name: 'copy-preload',
closeBundle() {
const src = fileURLToPath(new URL('./src/main/preload.js', import.meta.url))
const destDir = fileURLToPath(new URL('./dist-electron', import.meta.url))
const dest = join(destDir, 'preload.js')
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true })
copyFileSync(src, dest)
console.log('✅ Copied preload.js to dist-electron')
}
}
}</pre>
</div>
<p>在 plugins 中启用:</p>
<div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">plugins: [
// ...
copyPreloadPlugin()
]</pre>
</div>
</div>
<div class="code-block-extension-header">
<div>
<div>
<p><strong>备注</strong></p>
<blockquote>
<p>Electron 主进程可以使用 ESM,但 preload.js 必须使用 CommonJS(require),并且必须确保它被正确复制到输出目录。</p>
</blockquote>
<h3 data-id="heading-11">其他</h3>
<h4 data-id="heading-12">powershell 不能执行npm 命令</h4>
<blockquote>
<p>由于powershell 执行策略限制不能执行npm命令</p>
</blockquote>
<p>解决方案:修改策略</p>
<ul>
<li>在打开的窗口中,你可以先输入 <code>Get-ExecutionPolicy</code> 查看当前策略,通常会是 <code>Restricted</code>(禁止所有脚本)。</li>
<li>然后输入核心命令:<code>Set-ExecutionPolicy RemoteSigned -Scope CurrentUser</code>。</li>
<li>完成后,可以再次输入 <code>Get-ExecutionPolicy</code> 检查是否已变为 <code>RemoteSigned</code>,此时就可以执行命令了。</li>
</ul>
<h3 data-id="heading-13">总结</h3>
<p>Electron 在开发环境下逻辑清晰,编写方便;但打包、工具链配置等方面却相当繁琐,相当于手动组装一套完整的构建流程。</p>
</div>
</div>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19465833
頁:
[1]