背景

需求推进技术探索,技术实现需求设计。刚开始本来是用 vue 做一个 web 项目,随着开发推进,产品要求做成桌面端应用。。。
想到了用 Electron 框架构建桌面应用程序,查了一下大多都是使用 Electron 一步步构建项目,把已有 web 项目打包成桌面应用文档比较少,不过也找到一篇不错的文档,下面记录一下踩坑过程。

参考文章

Electron 文档
Electron API 文档
利用electron和electron-builder把前端web项目生成桌面程序

实现过程

准备工作

  1. 安装Node.js,使用最新的LTS版本
  2. 打包后的 web 项目
  3. 开发之前最好把 Electron 官方文档看一下,官方文档还是最权威的

配置淘宝镜像

原镜像下载可能太慢或下载失败,可将 npm 和 electron 的镜像地址都切换为淘宝镜像,cmd 命令输入:

npm config edit

将下面的地址复制、粘贴、保存、关闭

registry=http://registry.npm.taobao.org/
electron_mirror=https://npm.taobao.org/mirrors/electron/
electron-builder-binaries_mirror=https://npm.taobao.org/mirrors/electron-builder-binaries/

Electron 项目结构

.
├─icons                     # icon 图标目录
├─node_modules
├─h5                        # vue 资源
├─gulpfile.js               # gulp 配置文件
├─main.js                   # 应用程序的入口文件
├─package.json
├─README.md

创建应用程序

新建目录并进入

# windows
mkdir my-electron-app && cd my-electron-app

复制的官方文档,运行居然报错,PowerShell 提示:标记 “&&” 不是此版本中的有效语句分隔符

将命令行语句中的 && 改为分号 ; 就好了

# windows
mkdir my-electron-app; cd my-electron-app

创建 package.json 文件

{"name": "my-electron-app","version": "1.0.0","author": "freeman","description": "electron app","private": false,"scripts": {"view": "cross-env ELECTRON_ENV=dev electron .","start": "cross-env ELECTRON_ENV=dev .\\node_modules\\.bin\\gulp watch:electron","build": "cross-env ELECTRON_ENV=dep electron-builder"},"main": "main.js","build": {"productName": "my electron app","appId": "com.wss.app","directories": {"output": "builder"},"nsis": {"oneClick": false,"allowElevation": true,"allowToChangeInstallationDirectory": true,"installerIcon": "./h5/favicon.ico","uninstallerIcon": "./h5/favicon.ico","installerHeaderIcon": "./h5/favicon.ico","createDesktopShortcut": true,"createStartMenuShortcut": true},"win": {"target": ["nsis","zip"]},"files": ["h5/**/*","main.js"]},"devDependencies": {"cross-env": "^7.0.3","electron": "^16.0.7","electron-builder": "^22.14.5","electron-connect": "^0.6.3","gulp": "^4.0.2"}
}

package.json 代码解析

  • 启动命令设置
    这里我安装了cross-env,运行开发环境和打包时方便指定环境变量,同时引入了gulp配置热更新,编辑代码自动刷新应用
    热更新参考
"scripts": {"view": "cross-env ELECTRON_ENV=dev electron .","start": "cross-env ELECTRON_ENV=dev .\\node_modules\\.bin\\gulp watch:electron","build": "cross-env ELECTRON_ENV=dep electron-builder"
},
  • 设置入口文件为main.js

  • 打包相关配置

"build": {"productName": "my electron app",  // 项目名 这也是生成的exe文件的前缀名"appId": "com.wss.app",  // 包名  "directories": {  // 输出文件夹"output": "builder"}, "nsis": {"oneClick": false,  // 是否一键安装"allowElevation": true,  // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。"allowToChangeInstallationDirectory": true,  // 允许修改安装目录"installerIcon": "./h5/favicon.ico",  // 安装图标"uninstallerIcon": "./h5/favicon.ico",  // 卸载图标"installerHeaderIcon": "./h5/favicon.ico",  // 安装时头部图标"createDesktopShortcut": true,  // 创建桌面图标"createStartMenuShortcut": true  // 创建开始菜单图标},"win": {"target": ["nsis","zip"]},"files": ["h5/**/*",  // 要打包的目录"main.js"]
}

关于Electron项目出现白屏,控制台输出 Failed to load resource: net::ERR_FILE_NOT_FOUND 问题

  • npm run start运行时出现:
    执行npm run start,窗口出现白屏,控制台输出找不到资源,可能是 vue 项目的publicPath路径配置不对(vue 项目通过 npm run build 打包以后页面没有内容)
  • npm run build打包之后出现白屏:
    files 里面要打包的目录可能指定错了,没有把我们要打包的目录指定对,electron-build 就不会将build文件夹打包进去 app.asar 文件里,这样安装完.exe文件之后,控制台可能输出Not allowed to load local resource:,桌面程序打开白屏。

参考:electron-builder 打包后 出现Not allowed to load local resource:

  • 安装 npm 包
    我是安装的最新版Node.jsnpm依赖我都是安装在devDependencies里,如果刚开始安装在dependencies,打包的时候也会提示放到devDependencies
"devDependencies": {"cross-env": "^7.0.3","electron": "^16.0.7","electron-builder": "^22.14.5","electron-connect": "^0.6.3","gulp": "^4.0.2"
}

创建 gulpfile.js

因为想要实现热更新,安装了gulp,还需要创建gulpfile.js配置文件,这里只监听了main.js

const gulp = require('gulp');
const electron = require('electron-connect').server.create();gulp.task('watch:electron', function () {electron.start();gulp.watch(['./main.js'], electron.restart);
});

引入 web 项目

vue 项目路由模式使用hash,将打包后的 vue 资源放到 electron 项目根目录

创建 main.js 文件

const {electron, app, BrowserWindow, Menu, screen
} = require('electron')
const path = require('path')// app.isPackaged 如果应用已打包,则返回的属性,此属性可用于区分开发和生产环境
const isDevelopment = !app.isPackaged;
if(isDevelopment) {try {const client = require('electron-connect').client;} catch (err) {}
}// 屏蔽安全告警在console控制台的显示
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'if (process.mas) app.setName('My Electron')let mainWindowfunction createWindow() {const displayWorkAreaSize = screen.getAllDisplays()[0].workAreaconst windowOptions = {x: displayWorkAreaSize.x,  // 窗口相对于屏幕的左偏移位置.默认居中y: displayWorkAreaSize.y,  // 窗口相对于屏幕的顶部偏移位置.默认居中width: displayWorkAreaSize.width,height: displayWorkAreaSize.height,minWidth: 1220,minHeight: 780,show: false,// movable: false,  // 窗口是否可以拖动// maximizable: false,  // 窗口是否可以最大化// titleBarStyle: 'hidden',frame: true,  // 指定 false 来创建一个 Frameless Windowtitle: app.getName(),icon: path.join(__dirname, '/icons/icon.png'),  // 托盘图标hasShadow: false,  // 窗口是否有阴影autoHideMenuBar: true,  // 自动隐藏菜单栏webPreferences: {devTools: process.env.ELECTRON_ENV == "dev",// 是否开启 DevToolswebSecurity: true,  // //允许跨域plugins: true,  // 是否开启插件支持experimentalFeatures: true,  // 开启 Chromium 的 可测试 特性experimentalCanvasFeatures: true,  // 开启 Chromium 的 canvas 可测试特性minimumFontSize: 10,},}if (process.platform === 'linux') {windowOptions.icon = path.join(__dirname, '/h5/favicon.ico')}mainWindow = new BrowserWindow(windowOptions)mainWindow.loadURL(path.join('file://', __dirname, '/h5/index.html'))// 设置窗口默认最大化mainWindow.maximize()// 设置用户是否可以调节窗口尺寸// mainWindow.setResizable(false)mainWindow.show()mainWindow.on('closed', function () {mainWindow = null})process.env.ELECTRON_ENV == "dev" && client.create(mainWindow);
}app.on('ready', function () {const menu = Menu.buildFromTemplate(template)Menu.setApplicationMenu(menu) // 设置菜单部分createWindow()
})app.on('window-all-closed', function () {if (process.platform !== 'darwin') app.quit()
})app.on('activate', function () {if (mainWindow === null) createWindow()
})app.on('browser-window-created', function () {let reopenMenuItem = findReopenMenuItem()if (reopenMenuItem) reopenMenuItem.enabled = false
})app.on('window-all-closed', function () {let reopenMenuItem = findReopenMenuItem()if (reopenMenuItem) reopenMenuItem.enabled = trueapp.quit()
})/*** 注册键盘快捷键*/
let template = [{label: '操作',submenu: [{label: '回到系统入口',accelerator: 'CmdOrCtrl+Q',click: function (item, focusedWindow) {if (focusedWindow.id === 1) {BrowserWindow.getAllWindows().forEach(function (win) {if (win.id > 1) {win.close()}})}focusedWindow.loadURL(path.join('file://', __dirname, '/h5/index.html'))}}, {label: '复制',accelerator: 'CmdOrCtrl+C',role: 'copy'}, {label: '粘贴',accelerator: 'CmdOrCtrl+V',role: 'paste'}, {label: '重新加载',accelerator: 'CmdOrCtrl+R',click: function (item, focusedWindow) {if (focusedWindow) {// on reload, start fresh and close any old// open secondary windowsif (focusedWindow.id === 1) {BrowserWindow.getAllWindows().forEach(function (win) {if (win.id > 1) {win.close()}})}focusedWindow.reload()}}}]},{label: '窗口',role: 'window',submenu: [{label: '切换全屏',accelerator: 'F11',role: 'togglefullscreen',// click: function (item, focusedWindow) { // 自定义 click 和使用 role 效果是一样的//   if (focusedWindow) {//     focusedWindow.setFullScreen(!focusedWindow.isFullScreen());//   }// }}, {label: '最小化',accelerator: 'CmdOrCtrl+M',role: 'minimize'}, {label: '关闭',accelerator: 'CmdOrCtrl+W',role: 'close'}]},{label: '帮助',role: 'help',submenu: [{label: '意见反馈',click: function () {electron.shell.openExternal('https://www.electronjs.org/zh/docs/latest/api/app')}}]}
]function findReopenMenuItem() {const menu = Menu.getApplicationMenu()if (!menu) returnlet reopenMenuItemmenu.items.forEach(function (item) {if (item.submenu) {item.submenu.items.forEach(function (item) {if (item.key === 'reopenMenuItem') {reopenMenuItem = item}})}})return reopenMenuItem
}// 设置是否显示 DevTools
if(process.env.ELECTRON_ENV == "dev") {template[1].submenu.push({label: '切换开发者工具',role: 'toggleDevTools'})
}// 针对Mac端的一些配置
if (process.platform === 'darwin') {const name = electron.app.getName()template.unshift({label: name,submenu: [{label: '退出',accelerator: 'Command+Q',click: function () {app.quit()}}]})// Window menu.template[3].submenu.push({type: 'separator'}, {label: 'Bring All to Front',role: 'front'})// addUpdateMenuItems(template[0].submenu, 1)
}// 针对Windows端的一些配置
if (process.platform === 'win32') {const helpMenu = template[template.length - 1].submenu// addUpdateMenuItems(helpMenu, 0)
}

main.js 代码解析

main.js配置项可对照 Electron API 文档设置。本项目是把已有的 vue 项目打包,不是完全开发 Electron 应用,窗口菜单选项只能配置,不方便完全自定义。

  • 运行时启动热更新
// main.js
process.env.ELECTRON_ENV == "dev" && client.create(mainWindow);
  • 配置程序托盘图标
    不配置图标将使用系统默认的
function createWindow() {const windowOptions = {// ...icon: path.join(__dirname, '/icons/icon.png'),  // 托盘图标// ...}
}

配置图标这里需要注意,根据官方文档 nativeImage说明,eletron 可以设置大小为 32x32、64x64 。。。等一些尺寸的图标,但是同样指定一个 6464 的png,通过npm run build打包发现报错 (必需为256256),同时还发现只添加一张照片也不行,会报错,于是指定了三张不同尺寸的 icon 图片。
build 报错截图:
icons 目录截图:

  • 指定index.html位置
mainWindow.loadURL(path.join('file://', __dirname, '/h5/index.html'))

启动和打包应用程序

预览

npm run start

打包,打包输入目录在package.json中配置

npm run build

记录

打包之后,报错 A JavaScript error occurred in the main process

查了好久没有发现相关的问题,那就一定是项目中的配置存在问题,

通过图片发现是打包之后electron-connect报错,报错原因是因为electron-connect在开发环境安装的包,打包之后是不会安装的,生产引入未安装的包会报这种错,所以引入时要先判断下环境。

// main.js// app.isPackaged 如果应用已打包,则返回的属性,此属性可用于区分开发和生产环境
const isDevelopment = !app.isPackaged;
if(isDevelopment) {try {const client = require('electron-connect').client;} catch (err) {}
}

前端web项目使用electron和electron-builder生成桌面应用相关推荐

  1. 在HBuilder创建前端web项目时,图片加载不出来,怎么解决?

    在HBuilder创建前端web项目时,图片加载不出来,如下: 解决方法: 将图片文件路径减少,可正常显示,如图:

  2. 一文搞懂使用Nginx服务器部署前端Web项目

    1 Nginx简介与安装 概要 Nginx 简介 Nginx 架构说明 Nginx 基础配置与使用 1.1 Nginx 简介 Nginx是一个高性能WEB服务器,除它之外Apache.Tomcat.J ...

  3. 08 Tomcat+Java Web项目的创建和War的生成

    1.web服务器软件: 服务器:安装了服务器软件的计算机 服务器软件:接收用户的请求,处理请求,做出响应 * web服务器软件:接收用户的请求,处理请求,做出响应. 在web服务器软件中,可以部署we ...

  4. 使用Electron将Web项目打包成windows桌面应用

    1.从官网下载node.js并安装. 2.有不少人反映使用此命令安装Electron不成功,可能是国外镜像的问题. npm install --save-dev electron-prebuilt 3 ...

  5. 【Electron】使用Electron将web项目打包成桌面应用程序

    目录 一.所需环境&打包前准备 1.安装node.js 2.安装electron 3.web项目 二.打包过程 1.打包配置 2. 安装打包器 3.执行打包命令: Electron是由GitH ...

  6. electron打包web项目之stackedit实战——反面教材

    很抱歉,误导大家了.electron只支持客户端web项目,package.json里的scripts字段太误导人了.让我傻傻地以为真的可以在里面写一个 node server.js呢.辛亏我照着自己 ...

  7. 前端趋势榜:上周最有意思、又实用的 10 大 Web 项目 - 210821

    大家好,我是你们的 猫哥,那个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 平时如何发现好的开源项目,可以看看这篇文章:GitHub 上能挖矿的神仙技巧 - 如何发现优秀开源项目 1. shiki Shiki ...

  8. 前端趋势榜:上周最有意思、又实用的 10 大 Web 项目 - 210924

    大家好,我是你们的 猫哥,那个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 平时如何发现好的开源项目,可以看看这篇文章:GitHub 上能挖矿的神仙技巧 - 如何发现优秀开源项目 1. Thief Thief ...

  9. 《从零构建前后分离的web项目》准备 - 前端了解过关了吗?

    前端基础架构和硬核介绍 技术栈的选择 首先我们构建前端架构需要对前端生态圈有一切了解,并且最好带有一定的技术前瞻性,好的技术架构可能日后会方便的扩展,减少重构的次数,即使重构也不需要大动干戈,我通常选 ...

最新文章

  1. 13接口登记案例 把对象赋值给接口
  2. 1040 Longest Symmetric String (25 分)【难度: 一般 / 知识点: 最长回文子串】
  3. 在ABAP中将负号提前的方法
  4. 娃哈哈困境:做好了生意,没做好品牌
  5. 过拟合与模型调优(part2)--重抽样技术
  6. ElementUI+VUE 日期控件禁用用法
  7. python到底能干啥-Python到底可以干什么?主要应用领域
  8. adb ps shell 查看进程,如何使用ADB命令检测正在运行的应用程序
  9. 应用RSACryptoServiceProvider类轻松实现RSA算法
  10. 冲刺高阶ADAS市场,毫米波雷达多元侦测整合成主流趋势
  11. 阿里云OSS 使用内网地址上传文件
  12. als算法参数_协调过滤算法之ALS
  13. 尚硅谷JAVA基础笔记吐血整理
  14. shell 004 __小斌文档 |数组+函数
  15. python 多线程 代理 爬取 豆果美食app
  16. DSPE-PEG1-COOH可与伯胺基反应的反应性磷脂PEG共轭物之一
  17. 无招胜有招——面向对象设计模式随笔,
  18. AI经典书单:入门人工智能该读哪些书?
  19. Ubuntu Linux 查看、编辑、比较二进制文件
  20. 分享 10 个常见的 CSS 页面布局代码片段

热门文章

  1. onegreen的绿软word2003绿色版删除不掉的解决方案
  2. VS调试状态下断点总是进不去,不能进断点调试解决办法
  3. Java集合框架(List)
  4. 【MD5】MD5校验文件的正确性
  5. 操作系统文件管理实现思路
  6. 线性代数的一些知识点
  7. 工业基础类IFC—如何阅读物理文件
  8. od重新下载与crackme005记录
  9. 【Three.js入门】纹理加载进度、环境贴图、经纬线映射贴图与高动态范围成像HDR
  10. CSS代码中的属性,盒子模型