博客 / 詳情

返回

Electron 的西天取經

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

我本身是做 Web 開發的,最近需要寫個小工具,調用一些系統 API,就選擇了比較成熟的 Electron。結果業務代碼寫了三小時,環境配置、鏡像、文件引用、打包路徑、體積過大、文件被鎖定……各種問題卻折騰了將近三天。寫下這篇筆記,記錄這一路的坎坷。

第一難:官方文檔搭不起項目

按照官方説明初始化項目:

npm init
npm install electron --save-dev

結果第一步就卡住了——裝不上

初步猜測是網絡問題。雖然開梯子能解決,但不想一直掛着。查閲文檔後發現,Electron 在國內需要配置鏡像。

於是動手配置:

  1. 找到 .npmrc 文件,路徑可通過 npm config list 查看 user 字段;
  2. 加入鏡像地址:electron_mirror=https://npmmirror.com/mirrors/electron/

小結

國內開發環境配置鏡像算是常規操作,只是初次接觸時容易讓人心煩。

第二難:引入 Vue 與 Vite

官方示例雖然能運行,但沒有熱更新,開發效率低。既然選擇了 Electron,自然要利用前端生態,於是決定引入 Vue 和 Vite,配合 vite-plugin-electron 替換原有結構。

思維方式需要轉變:原本是加載本地 HTML 文件,現在則要加載 Vite 啓動的本地服務:win.loadURL('http://localhost:3000')

另一個問題是開發環境與生產環境的差異,需要引入環境變量。最終使用 cross-env 實現:

"dev": "cross-env NODE_ENV=development vite dev",

小結

邏輯上並不複雜,關鍵在於理解 Electron 分為主進程與渲染進程,最終前端跑的還是編譯後的 HTML 與 JS。理清整個流程後,內部實現就可以靈活替換。

第三難:如何調試?

頁面跑起來了,但怎麼打開開發者工具?後端代碼如何打斷點?

首先在創建 BrowserWindow 時加上:

win.webContents.openDevTools({ mode: 'detach' })

這樣啓動時就會自動打開調試工具。如果不小心關掉了,還可以用 Ctrl + Shift + I 重新打開。

主進程調試需要在 VSCode 中配置 launch.json,雖然不復雜,但配置起來略繁瑣:

{
    "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"]
        }
    ]
}

同時 Vite 配置也要配合調整端口與調試參數。

小結

前端調試工具的打開方式藏在文檔深處,最後還是靠博客文章才找到;主進程調試則要配置 VSCode 和 Vite 插件,整套流程配下來,比寫業務邏輯還耗時。

第四難:electron-forge 打包初體驗

一開始使用的是官方推薦的 electron-forge,但體驗並不理想:

  1. 直接生成 exe,沒有安裝引導,也沒有卸載入口;
  2. 雙擊直接運行,還會附帶一個無説明的更新進程;
  3. 配置引導進程不生效;
  4. 內存佔用高達 600MB,明顯是把 node_modules 全打包了;
  5. 中文文件名在安裝後變成亂碼;
  6. 打包時 asar 文件被佔用,必須殺掉所有 Electron 進程才能繼續。

小結

或許是我沒配置對,但整體使用體驗確實不佳。

第五難:electron-builder 打包卡在下載

於是換用 electron-builder,結果打包時卡在三個依賴包的下載:

  • winCodeSign-2.6.0
  • nsis-3.0.4.1
  • nsis-resources-3.4.1

又是網絡問題。有兩種解決辦法:

手動下載並放置緩存

將下載的包解壓到以下路徑:

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

配置鏡像

在 .npmrc 中加入:

electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

第六難:打包流程未完成

打包過程沒有報錯,但 release 文件夾沒有生成,流程未完整執行。

排查後發現,主進程入口不是 ./dist/main/main.js,而是由 Vite 插件控制的 ./dist-electron/main.js

小結

入口路徑的理解偏差,讓成功只差一步,卻耗費不少時間。

第七難:打包時文件被佔用

又遇到文件佔用錯誤:

EBUSY: resource busy or locked, unlink '.../dist/win-unpacked/resources/app.asar'

原因是 vite build 和 electron-builder 同時操作 dist/ 目錄,導致文件被鎖定。

解決方案是讓它們使用不同的輸出目錄。修改 Vite 配置:

outDir: fileURLToPath(new URL('./renderer-dist', import.meta.url))

同時調整 electron-builder.config.js 中的 files 配置:

files: ['renderer-dist/**/*', ...]

但打包後仍然沒有生成 release 文件夾,日誌顯示輸出在 dist\electron-deploykit Setup 0.0.1.exe,似乎配置文件未生效。

檢查 dist\builder-effective-config.yaml,確認配置未加載。嘗試修改配置文件名、加入日誌打印、甚至回退版本,均無效。

最後將配置文件改為 electron-builder.config.yml,居然成功了。

第八難:包體積過大

一個簡單頁面打包後居然佔用了 700MB。

asar 設為 false 後,發現果然是 node_modules 被全部打包。於是在配置中將其排除:

files:
  - ./renderer-dist/**/*
  - ./dist-electron/**/*
  - ./package.json
  - '!./**/*.map'
  - '!node_modules/**/*'

第九難:ESM 與 CommonJS 模塊衝突

打包安裝後程序無法啓動,日誌顯示模塊引用錯誤。由於代碼被壓縮,難以定位問題。

取消壓縮後,發現是 const windowManager = require('/windowManager') 這種 CommonJS 寫法在 Vite 中不被支持。

Electron 官方示例使用 CommonJS,但 Vite 默認使用 ESM。最終將所有模塊改為 ESM 寫法。

也可嘗試使用 vite-plugin-commonjs 插件轉換,但我沒有實際驗證。

第十難:preload.js 未被打包

開發環境運行時出現錯誤:

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'

檢查發現 dist-electron 下沒有 preload.js

於是在 Vite 配置中加入自定義插件,手動複製 preload 文件:

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')
        }
    }
}

在 plugins 中啓用:

plugins: [
    // ...
    copyPreloadPlugin()
]

備註

Electron 主進程可以使用 ESM,但 preload.js 必須使用 CommonJS(require),並且必須確保它被正確複製到輸出目錄。

其他

powershell 不能執行npm 命令

由於powershell 執行策略限制不能執行npm命令

解決方案:修改策略

  • 在打開的窗口中,你可以先輸入 Get-ExecutionPolicy 查看當前策略,通常會是 Restricted(禁止所有腳本)。
  • 然後輸入核心命令:Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
  • 完成後,可以再次輸入 Get-ExecutionPolicy 檢查是否已變為 RemoteSigned,此時就可以執行命令了。

總結

Electron 在開發環境下邏輯清晰,編寫方便;但打包、工具鏈配置等方面卻相當繁瑣,相當於手動組裝一套完整的構建流程。

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.