700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 前端web项目使用electron和electron-builder生成桌面应用

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

时间:2020-12-19 19:46:29

相关推荐

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

背景

需求推进技术探索,技术实现需求设计。刚开始本来是用 vue 做一个 web 项目,随着开发推进,产品要求做成桌面端应用。。。

想到了用 Electron 框架构建桌面应用程序,查了一下大多都是使用 Electron 一步步构建项目,把已有 web 项目打包成桌面应用文档比较少,不过也找到一篇不错的文档,下面记录一下踩坑过程。

参考文章

Electron 文档

Electron API 文档

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

实现过程

准备工作

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

配置淘宝镜像

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

npm config edit

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

registry=http://registry./electron_mirror=/mirrors/electron/electron-builder-binaries_mirror=/mirrors/electron-builder-binaries/

Electron 项目结构

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

创建应用程序

新建目录并进入

# windowsmkdir my-electron-app && cd my-electron-app

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

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

# windowsmkdir 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('/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}// 设置是否显示 DevToolsif(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.jsprocess.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) {}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。