前言
最近开发了一个新需求 使用 electron
开发桌面应用 整理一下心酸史和一些坑,供大家参考。废话不多说,进入正题
项目构建篇
一、技术要求
- electron13.x
- vue3.x
- ts4.x
- node version '>=14.0.0'
二、创建 v3 + ts 项目
vue create electron-vue3-ts
复制代码
- 如下图所示,选择自定义
- 选择一些项目基本配置
- vue版本 这里选择 3.x
- eslint 大家也可以自行选择其他 我这里选择只校验error
- 整体基本配置最终如下
- 开始自动构建
至此 vue + ts 项目搭建完成
三、v3集成 Electron
目前市面上现有的集成方案有两种:
由于 electron-vue
许久未在更新,判定为不在维护,所以技术选择 Vue CLI Plugin Electron Builder
坑一:直接执行vue add electron-builder报错如下 node版本有要求
正确的操作应该是
确保本地 node version '>=14.0.0'
npm install vue-cli-plugin-electron-builder -D
vue add electron-builder
复制代码
选择 13 的版本 继续安装 安装完成以后项目整体目录结构如下:
安装完成以后 发现控制台输出一个警告
WARN
It is detected that you are using Vue Router.
It must function in hash mode to work in Electron.
Learn more at https://goo.gl/GM1xZG
复制代码
大致意思就是 在electron中 vue 只能使用 hash
模式。history
模式不支持 修改如下
import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router'
const router = createRouter({
history: process.env.IS_ELECTRON ? createWebHashHistory(process.env.BASE_URL) : createWebHistory(process.env.BASE_URL),
routes
})
复制代码
修改完毕后,启动项目
npm run electron:serve
复制代码
启动过程总会发现 需要等待好长一段时间项目才能起来 这是因为installExtension
方法会持续请求安装VUEJS3_DEVTOOLS
,然后导致项目启动非常慢,必须等它五次请求超时后,才能启动项目 所以可以把这一段代码注释掉不执行
// background.ts
// 第一部分,这行代码注释掉
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
// 第二部分,这几行代码注释掉
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS3_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
复制代码
至此 electron + v3 + ts 项目就搭建成功了
项目实战篇
如图,整体项目结构基本没什么大的改变,基本按照正常vue 项目开发就可以了,只不过需要了解一下electron里面主进程和渲染进程
一、Electron
中的主进程和渲染进程
什么是主进程
- electron项目启动的时候运行main.js的进程就是主进程
- 一个项目有且只有一个主进程
- 创建窗口等所有系统事件都要在主进程中进行
简单的说就是我们的electron项目的主进程只有一个, 主进程的执行代码需要写到main.js中, 所有跟系统事件相关的代码统统都要写在这里
什么是渲染进程
- 我们每创建一个web页面都会创建一个渲染进程
- 每个web页面运行在它自己的渲染进程中
- 每个渲染进程是独立的, 它只关心它所运行的页面
简单理解就是一个web页面一个渲染进程
主进程和渲染进程的区别
- 主进程通过BrowserWindow创建页面
- 每个BrowserWindow实例都在自己的渲染进程中运行, 当BrowserWindow实例被销毁后, 相应的渲染进程也会被终止
主进程和渲染进程怎么通信
- 使用
ipcRenderer
和ipcMain
模块发送消息来解决通信问题 - 主进程代码
const { ipcMain } = require('electron')
// 监听渲染进程发送的消息
ipcMain.on('renderer-msg', (event, arg) => {
console.log(arg) // prints '帮我创建一个新的页面'
event.reply('main-msg', '好的'); // 给渲染进程回复消息
})
复制代码
- 渲染进程代码
const { ipcRenderer } = require('electron')
// 监听主进程发送的消息
ipcRenderer.on('main-msg', (event, arg) => {
console.log(arg) // prints '好的'
})
// 给主进程发消息
ipcRenderer.send('renderer-msg', '帮我创建一个新的页面')
复制代码
更多关于 electron 的知识 可以查看官方文档 基本都能找到答案
坑二:在渲染进程中使用electron 直接在vue页面里面引用会报错
Uncaught ReferenceError: __dirname is not defined
复制代码
此时百度一搜 大部分给出的结局方案就是 在项目根目录中创建vue.config.js
文件,添加以下内容
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
}
}
复制代码
添加配置之后 再重新运行项目,发现问题解决了 但是这个时候如果直接在浏览器打开访问的话你会发现 项目还是报错 在electron中并没有问题 另一种解决方案就是使用preload
预加载脚本
二、Electron
preload预加载
来看看electron 官方介绍
preload
String (可选) -在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入node的全局引用标志preload
是BrowserWindow类的参数webPreferences的一个可选设置项,preload
脚本的加载时机就是窗口建立后,页面加载之前。若是有人问,若是不挪用loadURL方式,不加载页面,preload
脚本会加载吗?谜底是会,但有什么用呢?你起个壳子不给人家看页面是什么鬼?不管这些,主要的是我们明白这个加载时机就好了。- 然后还要清楚一点,
preload
脚本是运行在渲染进程中的。再有一点就是,preload
脚本中可以调用window工具(渲染进程其实就是起了个浏览器壳子),preload
脚本运行在渲染进程,提前于页面和其他所有js的加载,又能调用Node API;脚本文件路径为绝对路径,当node integration关闭时,预加载的脚本将从全局范围重新引入node的全局引用标志:联系前面两点明白就好了。
那么,到底什么是预加载?
预加载就是:某一个渲染进程,在页面加载之前加载一个本地脚本,这个脚本能调用所有Node API、能调用window工具。用法如下:
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
复制代码
知道了什么是preload
那么咱们回到上一个问题 如何使用preload
解决报错
- 首先在 src 目录下创建一个preload.ts文件
preload.ts
import { ipcRenderer } from 'electron'
(window as any).ipcRenderer = ipcRenderer
复制代码
- 然后在vue.config.js配置加载preload.ts
module.exports = {
pluginOptions: {
electronBuilder: {
// nodeIntegration: true,
preload: 'src/preload.ts'
}
}
}
复制代码
- 然后在background.ts配置加载preload.js
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: (process.env.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
contextIsolation: !!process.env.ELECTRON_NODE_INTEGRATION,
enableRemoteModule: true,
webSecurity: false,
preload: path.join(__dirname, 'preload.js')
}
})
复制代码
- 最后在vue 文件中直接使用就可以了
setup() {
const ipcRenderer = (window as any).ipcRenderer
// 监听主进程发送的消息
ipcRenderer.on('main-msg', (event: any, arg: string) => {
console.log(arg) // prints '好的'
})
// 给主进程发消息
ipcRenderer.send('renderer-msg', '帮我创建一个新的页面')
}
复制代码
此时在浏览器打开访问的话你会发现 页面有东西了 但是js还是报错 在electron中并没有问题 至于如何解决在浏览器打开访问报错问题 我一时半会也没有找到什么别的好办法 欢迎大家在评论区留言
三、Electron
打包配置
vue 已经把 electron-builder 集成进来了 所以只需要在vue.config.js里面配置就可以了
module.exports = {
pluginOptions: {
electronBuilder: {
// nodeIntegration: true,
preload: 'src/preload.ts',
builderOptions: {
productName: "", // 项目名,也是生成exe文件的前缀名
appId: "", // 包名e7
copyright: "", // 版权信息
compression: "store", // "store" | "normal" | "maximum" 打包压缩情况(store 相对较快),store 39749kb, maximum 39186kb
directories: {
output: "dist_electron" // 输出文件夹
},
asar: true, // asar打包
win: {
icon: "build/icons/icon.ico", // 图标路径
target: [
{
target: "nsis", // 安装应用
arch: [
// ia32 | x64 | armv7l | arm64
"ia32", // 即–arch=ia32, 32位操作系统,也可以在64位操作系统中安装
"x64" // 即–arch=x64, 64位操作系统,使用本架构打包无法再32位操作系统中安装
]
}
]
},
linux: {
icon: "build/icons",
category: "home",
target: [
{
target: "AppImage", // AppImage, snap, deb, rpm, freebsd, pacman, p5p, apk, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir.
arch: ["arm64"]
}
]
},
mac: {
icon: "build/icons/icon.icns",
target: [
{
target: "dmg"
}
]
},
nsis: {
oneClick: false, // 一键安装
// "guid": "xxxx", //注册表名字,不推荐修改
perMachine: false, // 是否开启安装时权限限制(此电脑或当前用户)
allowElevation: true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
allowToChangeInstallationDirectory: true, // 允许修改安装目录
installerIcon: "build/icons/icon.ico", // 安装图标
uninstallerIcon: "build/icons/icon.ico", //卸载图标
installerHeaderIcon: "build/icons/icon.ico", // 安装时头部图标
createDesktopShortcut: true, // 创建桌面图标
createStartMenuShortcut: true, // 创建开始菜单图标
shortcutName: "" // 图标名称
}
}
}
}
}
复制代码
mac图标生成 icns
- 准备一张 1024 * 1024 的png图片,命名为
icon.png
- 创建一个用来存放不同大小图片的临时目录 命名为
tmp.iconset
mkdir tmp.iconset
复制代码
- 把
icon.png
转为不同大小的图片,并放入tmp.iconset
中
sips -z 16 16 icon.png --out tmp.iconset/icon_16x16.png
sips -z 32 32 icon.png --out tmp.iconset/[email protected]
sips -z 32 32 icon.png --out tmp.iconset/icon_32x32.png
sips -z 64 64 icon.png --out tmp.iconset/[email protected]
sips -z 128 128 icon.png --out tmp.iconset/icon_128x128.png
sips -z 256 256 icon.png --out tmp.iconset/[email protected]
sips -z 256 256 icon.png --out tmp.iconset/icon_256x256.png
sips -z 512 512 icon.png --out tmp.iconset/[email protected]
sips -z 512 512 icon.png --out tmp.iconset/icon_512x512.png
sips -z 1024 1024 icon.png --out tmp.iconset/[email protected]
复制代码
- 通过
iconutil
生成icns文件
conutil -c icns tmp.iconset -o Icon.icns
复制代码
此时就得到了icon.icns
Apple图标图像
执行打包命令
npm run electron:build
复制代码
window图标生成 ico
坑三 切记不要直接将
.png结尾的图标直接改成
.ico打包的时候会报错
通过ico在线生成工具去生成一个就可以了 尺寸选择 256*256
再重新打包就可以了
npm run electron:build_win
复制代码
备注:本文是以mac
开发环境进行的打包 可以同时打mac
包和window
包;如果是window本进行打包 只能打window
包 相应的打包命令就是npm run electron:build
项目git地址
github.com/jifenjilian…
推荐使用 npm 安装 根目录创建.npmrc
文件 使用taobao
源进行依赖安装
electron_mirror=https://npm.taobao.org/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/
phantomjs_cdnurl=http://npm.taobao.org/mirrors/phantomjs/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
复制代码
总结
到此 electron + vue3 从项目搭建开始、到项目开发、再到最后的项目打包 整体流程基本就梳理出来了,相信看到这的小伙伴一定会有所收获。文中的不同之处,也欢迎大家在评论区留言讨论。