手动搭建 Electron-vue 开发环境
Electron 是一个使用 HTML、CSS、JavaScript、Node.js 来开发桌面应用程序的开源框架,它具有开发速度快和跨平台方便的优点。虽然打包的软件体积比较大,体验也不如 C++ 的 QT 和 C# 的 WPF,但是 Electron 只需要前端程序员就能开发,现在的 PC 软件大多数也是使用 Electron 开发。
Vue 是一个前端 Web 框架。使用 Vue 可以很方便的开发单页面应用,Vue 官方也提供了开箱即用的路由管理和状态管理插件,对于新手来说,Vue 是比较容易上手的框架。
之前我开发 Electron 使用了 Vue CLI Plugin Electron Builder ,这是一个能快速把 Electron 添加到 Vue 项目的 Vue 插件,但这个插件使用的打包工具可配置的选项比较少,官方的文档也不是太详细。
electron-vue 是一套配置好的使用 Vue + Electron 的项目样板,可以很方便的搭建 Vue + Electron 的项目,但是 electron-vue 使用的资源太老了,估计是很早以前就停止维护了,Electron 使用的还是 2.0.4 的版本,现在的 Electron 已经到 19.0.8 了,官方的帮助文档基本上不能用。
我这里会使用 Vue-cli 来初始化一个 Vue 项目,然后手动安装和配置 electron 和 electron-builder。
初始化 Vue
安装 Vue-cli:
npm install -g @vue/cli
初始化一个 Vue2 项目:
vue create projectName
其中 projectName
是项目名称,
我这里直接使用 Vue2 的默认配置:
Default ([Vue 3] babel, eslint)
> Default ([Vue 2] babel, eslint)
Manually select features
项目安装完成后使用 cd 项目名称
可以进入项目目录,使用 npm run serve
可以运行项目,使用 npm run build
可以编译打包项目。
安装 Electron
进入 Vue 项目目录后输入:
npm install --save-dev electron
安装 Electron。
安装完成后在 package.json
的 devDependencies
可以看到 Electron 的版本。
配置
可以在项目的 src
目录下创建一个 electron-main.js
作为 Electron 的入口文件,在 electron-main.js
中加入一些 Electron 的代码来创建一个窗口:
const {app, BrowserWindow} = require('electron');
let mainWindow; // 用来保存主窗口对象的引用
// 当 Electron 完成初始化并准备创建浏览器窗口时被调用
app.on('ready', () => {
// 创建主窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
// 加载页面文件
if (app.isPackaged) {
// 如果是打包好的就加载打包的 HTML 文件
mainWindow.loadFile('dist/index.html');
}else {
// 如果没有打包就直接从本地服务器加载
mainWindow.loadURL('http://localhost:9999/');
}
// 关闭事件
mainWindow.on('closed', () => {
mainWindow = null;
});
});
上面在 src
目录下创建了一个 electron-main.js
作为 Electron 的入口文件,下面在 package.json
的 main
中设置 Electron 的入口文件:
{
"name": "ocr-electron",
"version": "0.1.0",
"main": "src/electron-main.js",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.6.14"
}
}
上面的 package.json
只包含一部分内容,完整的 package.json
比较长,我会放到后面。
在 Electron 入口文件中,我会根据是否打包来选择页面文件,如果没有打包就从本地服务器来加载页面,如果已打包就加载 dist
目录的 index.html
,Vue 编译后的页面文件也在 dist
目录。
Vue 官方配置的项目打包完成后的 HTML script
如下:
<script src="/js/xxxx.js"></script>
在文件路劲最前面会有一个 /
,这种路劲在服务器环境是可以正常加载 JS 文件的,但如果在浏览器直接打开 HTML 文件就会出现找不到 JS 文件的情况,Electron 也找不到 JS 文件。
下面通过更改 vue.config.js
来使用正常的相对路劲加载 JS 文件,如果你的 Vue 项目目录下没有 vue.config.js
可以创建一个,在模块的对象根部加入 publicPath: './'
,下面是我的 vue.config.js
:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
publicPath: './',
devServer: {
port: 9999
}
})
不同版本的 Vue-cli 创建的项目 vue.config.js
的内容可能会有些不一样,publicPath: './'
加到对象根部就可以。
除了增加 publicPath
外,我这里还更改了本地服务器的端口,我在 Electron 入口文件设置的加载页面文件的地址是 http://localhost:9999/
,本地服务器端口我设置的也是 9999。
下面还需要在 package.json
中配置 Electron 的运行命令,在 script
中加入一个 start
命令,start
命令的值是 electron .
,如下:
{
"name": "ocr-electron",
"version": "0.1.0",
"main": "src/electron-main.js",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"start": "vue-cli-service build && electron ."
}
}
上面的 package.json
为了方便查看,还是只包含一部分内容。
下面尝试运行一下,输入:
npm run serve
Vue 的本地服务器启动后再开一个命令行,输入:
npm run start
Electron 检测到没有打包就会从 http://localhost:9999/
加载页面,如下:
Electron 成功运行。
如果渲染进程的页面内容改变,Electron 也会自动刷新。
测试
下面测试一下 Electron 的 API:
新版的 Electron 是不能在渲染进程使用 Electron API 的,也不推荐在渲染进程使用 Node 模块,而且在默认的 Webpack 中如果在 Vue 组件中引入 Node 模块也肯定会报错。
Electron 官方推荐的是把 Electron API 和 Node 模块放到主进程,渲染进程如果要使用 Electron API 和 Node 模块就使用 ipc 给主进程发送请求,由主进程来运行 Electron API 和 Node 模块。
渲染进程要和主进程通信就需要用到 Electron 的 ipcRenderer 模块,在渲染进程是不能直接引入 ipcRenderer 的,Electron 提供了一个预加载脚本,预加载脚本会在网页内容加载之前运行,预加载可以运行 Electron API ,也可以访问页面的 window
。
Electron 提供了一个 contextBridge
模块,在预加载脚本可以使用 contextBridge
把一部分 Electron API 暴露到 window
给渲染进程使用,渲染进程可以通过 window
来使用 Electron API。
下面在 Vue 项目的 src
目录创建一个 preload.js
作为预加载脚本,在 preload.js
中把 ipcRenderer
模块暴露给渲染进程使用:
const {contextBridge, ipcRenderer} = require('electron');
// 把 ipcRenderer 暴露给渲染进程
contextBridge.exposeInMainWorld('electronApi', {
ipcRenderer
});
contextBridge.exposeInMainWorld
的第一个参数是 API 名称,可以自定义,我定义的是 electronApi
。
在 Electron 入口文件创建 BrowserWindow
的时候,可以在 webPreferences
的 preload
设置预加载脚本的位置,如下:
const {app, BrowserWindow} = require('electron');
const path = require('path');
let mainWindow; // 用来保存主窗口对象的引用
// 当 Electron 完成初始化并准备创建浏览器窗口时被调用
app.on('ready', () => {
// 创建主窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
// 加载页面文件
if (app.isPackaged) {
// 如果是打包好的就加载打包的 HTML 文件
mainWindow.loadFile('dist/index.html');
}else {
// 如果没有打包就直接从本地服务器加载
mainWindow.loadURL('http://localhost:9999/');
}
// 关闭事件
mainWindow.on('closed', () => {
mainWindow = null;
});
});
要在预加载脚本中使用 contextBridge
暴露 Electron API,webPreferences
的 contextIsolation
也需要设置为 true
。
下面我需要实现在 Vue 组件中点击按钮后弹出 Electron 的消息对话框,App 组件:
<template>
<div id="app">
<button type="button" @click="showDialog">显示对话框</button>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
上面只有 显示对话框
的 button
是我新增的,其他元素都是 Vue 项目自动生成的,JavaScript 如下:
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
methods: {
showDialog() {
window.electronApi.ipcRenderer.send('show-dialog');
}
}
}
HelloWorld
组件也是 Vue 创建项目时自动生成的,主要看 showDialog
方法。
我在 preload.js
把 ipcRenderer
暴露到了 window
,这里就可以直接调用 ipcRenderer
和主进程通信。
主进程:
const {app, BrowserWindow, ipcMain, dialog} = require('electron');
const path = require('path');
let mainWindow; // 用来保存主窗口对象的引用
// 当 Electron 完成初始化并准备创建浏览器窗口时被调用
app.on('ready', () => {
// 创建主窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
// 加载页面文件
if (app.isPackaged) {
// 如果是打包好的就加载打包的 HTML 文件
mainWindow.loadFile('dist/index.html');
}else {
// 如果没有打包就直接从本地服务器加载
mainWindow.loadURL('http://localhost:9999/');
}
// 关闭事件
mainWindow.on('closed', () => {
mainWindow = null;
});
});
// 监听显示对话框请求
ipcMain.on('show-dialog', () => {
// 显示对话框
dialog.showMessageBox({
type: 'info',
title: 'Mr. Ma\'s Blog 博主',
message: '我的 Github 是 https://github.com/changbin1997',
buttons: ['确定', '取消']
});
});
测试效果如下:
成功弹出对话框。
打包
上面安装的 electron 可以运行,但是还不能打包为 exe 之类的可执行程序,打包需要安装 electron-builder :
npm install electron-builder --save-dev
安装完成后在 package.json
的 scripts
中添加打包命令:
{
"scripts": {
"pack": "vue-cli-service build && electron-builder --dir",
"dist": "vue-cli-service build && electron-builder"
}
}
添加完成后我的 package.json
的 scripts
如下:
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"start": "electron .",
"pack": "vue-cli-service build && electron-builder --dir",
"dist": "vue-cli-service build && electron-builder"
}
}
后面添加的 pack
和 dist
命令前半部分是 Vue 的编译命令,和 build
的内容是一样的,后半部分是 Electron 的打包命令。
运行 npm run pack
或 npm run dist
后,Vue 组件会被编译打包到 dist
目录,然后 Electron 会开始打包。
我在 Electron 入口文件 electron-main.js
中通过 app.isPackaged
来检测是否打包,如果已打包就从 dist
目录加载 index.html
页面文件。
打包之前还需要在 package.json
的 build
中配置软件信息和文件过滤,否则 electron-builder 会把 src
中的源码和 Vue 组件打包进去,可能会导致打包后的软件无法正常运行。
我的 package.json
中的 build
配置如下:
{
"build": {
"appId": "ocr-electron",
"productName": "ocr-electron",
"icon": "public/favicon.ico",
"copyright": "Copyright © 2022",
"compression": "maximum",
"asar": true,
"win": {
"icon": "public/favicon.ico",
"target": "nsis",
"legalTrademarks": "changbin1997"
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": false
},
"directories": {
"output": "release"
},
"files": [
"**/*",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
"!**/node_modules/*.d.ts",
"!**/node_modules/.bin",
"!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
"!.editorconfig",
"!**/._*",
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
"!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
"!**/{appveyor.yml,.travis.yml,circle.yml}",
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
"!**/src/components/**",
"!**/src/assets/**",
"!**/src/main.js",
"!**/src/App.vue",
"!**/public/index.html"
]
}
}
下面是一部分配置内容说明:
appId
: 软件的一个标识符productName
: 软件名称icon
: 软件图标copyright
: 版权信息compression
: 软件包的压缩级别win
: Windows 相关的配置win
>icon
: Windows 的软件图标win
>target
: 封装类型,我设置的nsis
是 exe安装包win
>legalTrademarks
: 商标信息nsis
: 安装包配置nsis
>oneClick
: 一键安装nsis
>perMachine
: 为这台计算机上的所有用户安装,也就是安装到Program Files
而不是用户目录nsis
>allowToChangeInstallationDirectory
: 是否允许更改安装位置nsis
>createDesktopShortcut
: 是否创建桌面快捷方式nsis
>createStartMenuShortcut
: 是否创建开始菜单快捷方式directories
>output
: 打包后的软件存放位置files
: 设置要打包的文件和文件过滤,其中**/*
是设置当前目录为程序目录,!
开头的是需要忽略的文件
有些参数的值我这里写的不是太详细,参数值说明也可以参考我之前写的 使用 electron-builder 打包 Electron 应用 ,更详细的配置说明可以看 electron-builder 官方文档 。
运行命令和打包参数都配置完成后就可以开始打包了,输入:
npm run dist
可以生成安装包和包含 exe 的软件目录。
输入:
npm run pack
可以生成包含 exe 的软件目录,不会生成安装包。
第一次打包 electron-builder 会下载平台所需的依赖,如果你在国内使用的话,建议开代理,否则可能会打包失败。
软件体积优化
我在上面的 files
配置中已经做了一些优化,README.md
、.idea
、.log
这些文件都会被忽略。
electron-builder 会根据 package.json
的项目依赖来打包文件,如果你的项目依赖被 Webpack 打包过,electron-builder 也会再打包一次,像 Vue、Bootstrap、Element UI 这一类被 Webpack 打包过的就没必要让 electron-builder 打包进去。
解决方法也比较简单,把 package.json
的 dependencies
中的项目依赖都放到 devDependencies
中,Webpack 只会打包项目中引入的模块,electron-builder 不会打包 devDependencies
中的模块。
安装模块的时候可以加 --save-dev
参数,安装完成后就会直接在 devDependencies
中。
一些不需要 Webpack 打包,但是又是项目依赖的模块,例如 SQLite 之类的还是需要放到 dependencies
中让 electron-builder 打包的,否则可能会出现找不到模块的情况。
把需要 Webpack 打包的项目依赖放到 devDependencies
中,再使用 electron-builder 打包,应该可以发现软件体积小了一些,软件的运行不会受到任何影响。
类似文章:
版权声明:本文为原创文章,版权归 Mr. Ma's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/914/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。