一、引言
在文章开始之前,需要先介绍一下webpack和electron;
Webpack:是一个用于现代 JavaScript 应用程序的静态模块打包工具。
Electron:Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。
当遇到需要将原本是 web 端的应用打包成桌面端的需求时,就可以采用 webpack+electron 的方式来开发桌面端,这种方式有以下四点好处:
-
原项目改动小,由于 electron 自带 Chromium,所以原项目几乎可以无缝迁移到 electron。
-
前端技术栈复用,electron 的渲染进程可以使用当下任何前端框架(如 vue、react)来构建应用的界面,同时 electron 集成了 node.js 原生能力,
可以方便的操作本地文件系统、调用本地程序、创建系统托盘、打开文件对话框等等,不需要再去学习 c#、c++ 语言,对前端开发十分的友好。 -
跨平台开发,一套代码可以打包多平台(windows、linux、macOS),这大大降低了开发和维护的成本,而且一套 HTML/CSS/JS 在多端上的表现也一致,
不需要针对不同的平台做专门的兼容处理。 -
易于打包和发布,electron-builder 可以打包生成 .exe(windows)、.dmg(macOS)、.AppImage(linux),方便在不同平台安装,通过
electron-updater 可以在线升级。

二、基本概念
- electron 的基本概念
Electron分为主进程、渲染进程。主进程的作用是启动应用、创建/销毁窗口、控制应用的生命周期、访问操作系统的API,管理全局数据、权限控制等;
渲染进程的作用是显示用户界面、处理用户交互,应用的功能基本都在渲染进程中实现。
主进程和渲染进程通过IPC(主进程中是ipcMain,渲染进程中是ipcRenderer),ipc的通信方式和事件总线十分类似:
主进程中接收消息:
// main.js
const { ipcMain, dialog } = require('electron');
ipcMain.on('open-file-dialog', (event, arg) => {
console.log('收到消息:', arg);
dialog.showOpenDialog({ /* ... */ });
});
渲染进程中发送消息:
// renderer.js
const { ipcRenderer } = require('electron');
ipcRenderer.send('open-file-dialog', 'some data');
主进程中发送消息:
// main.js
win.webContents.send('update-available', '新版本来了!');
渲染进程中接收消息:
// renderer.js
ipcRenderer.on('update-available', (event, msg) => {
console.log('主进程通知:', msg);
});
- webpack 的基本概念
webpack 的内容庞大,这里就简述一下 webpack 中入口(entry)、输出(output)、插件(plugin)、模式(mode)的概念。
- 入口起点指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图的开始。进入入口起点后,webpack 会 找出有哪些模块和库是入口起点(直接和间接)
依赖的。 - 输出属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是
./dist/main.js,其他生成文件默认放
置在./dist文件夹中。 - 插件用于执行打包优化,资源管理,注入环境变量等。
- 通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。
三、项目集成与目录结构
- 通过 npm 安装
electron、electron-builder、vue-cli-plugin-electron-builder到开发依赖下,自动更新需要安装
electron-updater - 建议在项目根目录下或
src文件夹下新建 electron 文件夹,将 electron 相关的脚本都放到此文件夹下my-app/ ├── dist/ # 渲染进程构建输出目录 ├── dist_electron/ # 桌面端构建输出目录 ├── src/ │ ├── electron/ # Electron 相关代码 │ └── ... ├── vue.config.js # vue-cli 配置文件 ├── electron-builder.yml # electron-builder 打包配置文件 ├── package.json └── ...
四、运行与打包
- 修改
package.json:{ "scripts": { "electron:serve": "vue-cli-service electron:serve", "electron:build": "vue-cli-service electron:build", } } - 在 electron 主进程脚本中加载相应的渲染进程的开发地址:
// src/electron/index.js mainWindow.loadURL('http://localhost:3000'); // 或者使用 webpack 的开发服务器地址变量 // mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL); - 执行
npm run electron:serve - 在
package.json中添加打包配置:{ "build": { "appId": "com.example.electronapp", "productName": "MyElectronApp", "directories": { "output": "release" }, "files": [ “./dist_electron/bundled” ], "mac": { "target": "dmg" }, "win": { "target": "nsis" }, "linux": { "target": "AppImage" } } } - 修改
vue.config.js:pluginOptions: { electronBuilder: { preload: 'src/electron/preload.js', # 向渲染进程提供 node.js API customFileProtocol: './', # 自定义文件的根目录 mainProcessFile: 'src/electrin/index.js', # 主进程的入口 nodeIntegration: true, }, }, - 执行
npm run electron:build,打包完成之后你将在文件夹中看到对应平台的安装包。
五、常见问题
-
打包后提示找不到
background.js在
package.json中添加"main": "background.js" -
打包后打开应用空白
在生产和开发环境下,需要加载的资源地址不同,可对主进程脚本中做如下修改:
// src/electron/index.js ... const isDevelopment = process.env.NODE_ENV === 'development' app.on("ready", () => { const mainApp = new BrowserWindow({ ... }) // 如果默认协议是 file,可将 app 改为 file mainApp.loadURL(isDevelopment ? process.env.WEBPACK_DEV_SERVER_URL : `app://./index.html`) }) -
preload.js在生产和开发环境下地址不一样,导致无法注入preload.js打包后,由于资源统一在asar下,
preload.js的路径会和开发环境下不同,可在主进程脚本中做如下修改:// src/electron/index.js ... const isDevelopment = process.env.NODE_ENV === 'development' app.on("ready", () => { const mainApp = new BrowserWindow({ ... preload: isDevelopment ? path.join(__dirname, '../src/electron/preload.js') : path.join(app.getAppPath(), 'preload.js'), }) }) -
macOS上每次安装都有安全提示或直接提示文件损坏无法安装
这是因为 macOS 的安全策略导致的。有两种解办法,一种是修改 macOS 的安全策略等级;另一种是打包 macOS 平台应用时,进行应用的公证。
在
package.json中添加配置:// package.json { "build": { ... "mac": { ... "hardenedRuntime": true, "entitlements": "build/entitlements.mac.plist", "entitlementsInherit": "build/entitlements.mac.plist" } "afterSign": "src/electron/notarize.js" } }在 build 文件夹下添加
entitlements.mac.plist文件// build/entitlements.mac.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.cs.allow-jit</key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> <key>com.apple.security.cs.allow-dyld-environment-variables</key> <key>com.apple.security.cs.disable-library-validation</key> <true/> </dict> </plist>执行
npm install -D @electron/notarize安装公证依赖,在src/electron/中新建notarize.js,并到
https://developer.apple.com/中申请开发者账号,创建与公证相关的证书等操作(可自行百度)。// src/electron/notarize.js const { notarize } = require('@electron/notarize') exports.default = async function notarizeMacos(context) { const { electronPlatformName, appOutDir } = context console.log('开始公证') if (electronPlatformName !== 'darwin') { console.log('非macos环境,停止公证') return } const appName = context.packager.appInfo.productFilename await notarize({ appBundleId: # 应用 bundleId, appPath: `${appOutDir}/${appName}.app`, appleId: # 苹果账号, appleIdPassword: # 应用专属密码, teamId: # 团队id, }) console.log('公证完成') } -
打包后的 macOS 安装包,有的电脑可以安装,有的缺安装后无法打开
这是由于苹果公司在使用自研 cpu 前一直使用的是英特尔的 cpu,这两种 cpu 的架构不同,一个是 arm64 一个是 x64;针对这个问题有两种解决方案,
一种是打包时指定 cpu 的架构npm run electron:build --mac --arm64、npm run electron:build --mac --x64, 另一种是
打双架构包,但是这会让应用体积变大,执行命令npm run electron:build --mac --universal或修改package.json中的打包配置:// package.json { "build": { ... "mac": { ... target: { target: "default", arch: ["universal"] } } } }
六、结语
vue-cli 已逐渐退出主流舞台,在当下的新项目中,我们更推荐使用现代构建工具如 Vite。配合社区成熟方案如 vite-electron,能够实现开箱即用的开发体验,无需手动配置繁琐的 Webpack、Babel 或热更新逻辑,极大提升了开发效率与项目可维护性。
不过,本文的主要背景是公司现有的老项目。在这种场景下,我们面临的首要任务是如何平滑地将既有的 Web 应用迁移为桌面端应用。综合评估了开发成本、学习曲线、技术栈一致性以及后续的可维护性,我们最终选择了使用 Electron 作为桌面端方案。
虽然 Electron 的应用体积相对较大,内存占用也较高,但它拥有成熟的生态、良好的文档支持,并且能够最大限度地复用前端项目已有的技术栈与代码结构。这对于一个追求稳定、可控、低风险迁移的项目来说,是一种非常稳妥而现实的技术选型。
希望本文能为你在类似的技术决策过程中提供一些参考,无论是面对旧项目的迁移,还是在探索前端能力在桌面平台的延展。理解技术选型背后的权衡,往往比选择某个具体工具本身更重要。
如果你在迁移过程中遇到具体的问题,欢迎留言或讨论,也欢迎你继续关注后续的进阶分享。
934



被折叠的 条评论
为什么被折叠?



