之前开发项目遇到前端的需求,我通常会选择Vue2,无他,只是在众多前端框架中,我比较熟悉Vue2,此前尝试学过一段时间React,被它的Hook恶心到了,会了又学,学了又忘,所以还是回到Vue的怀抱,至于React,日后再挑战。
本篇文章使用当前最新的前端技术体系来构建一个桌面应用,本篇只是记录项目搭建的过程并实现项目的打包与安装,先看效果图:
image.png我们使用Vite来构建Vue3项目,看了Webpack与Vite在Dev阶段时,项目启动的速度,果断弃用复杂的Webpack,虽然Vite目前在生态上还差Webpack一段距离,但却会是后续的发展方向。
不熟悉Vite的可看Vite原理浅析
Vite对Vue3有较好的支持,直接使用vue-ts创建Vue3项目则可,然后使用TypeScript作为项目的开发语言。
yarn create vite vite-project --template vue-ts
cd vite-project
yarn
yarn dev
正常安装后,项目会在3000端口运行,访问locclhost:3000则可以看到Vite+Vue3+TS的项目
yarn add -D electron electron-builder
通过yarn安装electron与electron-builder(electron打包工具),因为国内的环境,我们可以使用.yarnrc或直接为yarn设置镜像,但这里需要注意一点,就是镜像网站的变更,npm淘宝源会在2022-05-31要切换镜像网站域名,之前的域名不再提供服务(https://zhuanlan.zhihu.com/p/432578145),那么之前很多博文里提及的设置镜像的文章日后就不再生效了。
最终我还是使用官方源配合科学上网来解决electron安装的问题,你也可以参考安装Electron门道安装Electron。
Vite在开发是会使用Koa作为Web服务来提高我们开发效率,也是因为Koa,我们才可以直接在开发过程中访问localhost:3000,但在正式上线时,Vite会将代码、资料打包成静态文件。
Electron在不引入Vite+Vue3时,可以直接通过朴素的HTML、CSS、JS来构建页面,Vite+Vue3的引入让我们要考虑如何让Electron在不同开发情况下接入Vite+Vue3,比如开发时,Electron载入localhost:3000,而打包后,Electron载入Vite打包后的静态文件。
为了实现上面的目的,需要安装一些npm包:
yarn add -D concurrently cross-env wait-on
其中,concurrently用于并行执行多个命令,wait-on用于实现命令间的广告,cross-env用于设置命令运行时的环境变量,安装完后,将package.json的scripts修改成如下形式:
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"electron": "wait-on tcp:3000 && cross-env IS_DEV=true electron .",
"electron:dev": "concurrently -k \"cross-env BROWSER=none yarn dev\" \"yarn electron\"",
"electron:builder": "electron-builder",
"build:for:electron": "vue-tsc --noEmit && cross-env ELECTRON=true vite build",
"app:build": "yarn build:for:electron && yarn electron:builder"
},
开发时,通过yarn electron:dev启动electron项目,electron:dev指令会通过concurrently并行执行yarn dev与yarn electron,其中运行yarn dev时,通过cross-env设置了此次命令的环境变量,这里将BROWSER环境变量设置为none,这样vite就不会主动通过浏览器打开localhost:3000(但我们可以主动访问这个页面)。
yarn dev命令会运行vite服务,yarn electron会运行electron服务,该命令首先会通过wait-on等等3000端口的服务启动后再执行electron .,而3000端口正是vite服务默认启动的端口,如果vite启动服务时,发现3000端口被占有,会默认使用3001,此时yarn electron也需要相应的修改调整。
光在package.json的scripts中指定各种指令是没用的,还需要让npm指定入口文件,在package.json中设置main:
"main": "electron/electron.js",
然后再vite-project目录下创建electron文件夹,然后再文件夹中创建electron.js文件,在js文件中,使用electron创建桌面应用的窗口,代码如下:
const path = require('path');
const { app, BrowserWindow } = require('electron');
// 判断环境变量
const isDev = process.env.IS_DEV == "true" ? true : false;
function createWindow() {
// 创建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// 预加载
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
},
});
// 基于环境变量,判断窗口要载入的内容(即要显示的内容)
mainWindow.loadURL(
isDev
? 'http://localhost:3000' // 如果是开发环境,则载入vite服务
: `file://${path.join(__dirname, '../dist/index.html')}` // 如果是正式环境,则载入vite生成的index.html
);
// 如果是开发环境,则自动打开Chrome debugger工具
if (isDev) {
mainWindow.webContents.openDevTools();
}
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
});
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
electorn.js中主要的逻辑就是通过isDev环境变量判断要载入的内容,此外就是给app对象设置相应的监听回调函数,这块主要兼容MacOS环境下的情况,不赘述。但Electron预加载(preload)的功能可以聊一下。
上述代码中,通过webPreferences.preload属性,我们预加载了preload.js,该文件代码如下:
// 所有Node.JS API都可以使用
// 拥有与Chrome插件一样的沙箱(sandbox)环境
window.addEventListener('DOMContentLoaded', () => {
// 替换元素内容
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})
我们这里的preload.js虽然没做啥,但利用Electron preload我们可以做很多有趣的事情,有必要展开聊聊。
preload在Electron中不是新鲜事物,3.0版本就支持了,但因为早期的Electron在渲染进程(renderer)中可以直接使用Node.JS的API,preload就没太被关注,但在5.x以后,Electron渲染进程中默认不可以使用Node了,虽然可以通过配置打开对Node API的支持,但社区的态度慢慢转向禁用node,从而避免网页JS恶意使用Node的情况。
比如你开发的Electron具有访问互联网中网页的功能,如果渲染进程开启了Node,恶意网页中的JS就可以利用NodeJS直接对你的系统进行操作,这是非常危险的。
在对安全的考虑下,Electron官方不建议在渲染进程中开启Node API的支持,而是建议使用preload,Electron在渲染每个页面前都会执行preload指定的JS脚本,上述例子就是执行preload.js。
Electron让preload指定的JS脚本拥有早于网页JS执行的能力,且preload可以使用NodeJS,再加上它拥有Chrome插件一般的沙箱,利用Electron去访问第三方访问,再基于preload的能力去魔改这些网页的JS就是一条不错的路子(没错,可以往爬虫方向靠)。
编写好electron.js、preload.js以及package.json的main和scripts后,便可以在开发状态下运行electron项目了:
yarn electron:dev
当我们编写完业务逻辑后,需要打包出不同系统平台的应用,然后分发给用户,此时我们需要使用electron-builder,在package.json中添加如下内容:
"build": {
"appId": "com.my-website.my-app",
"productName": "MyApp",
"copyright": "Copyright © 2019 ${author}",
"mac": {
"category": "public.app-category.utilities"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"files": [
"dist/**/*",
"electron/**/*"
],
"directories": {
"buildResources": "assets",
"output": "dist_electron"
}
}
上面都是electron-builder的基本配置,可以参考:https://www.electron.build/configuration/configuration
配置好后,通过yarn app:build便可以打包应用了。
主要观察输出内容,打包应用时会从github中下载相应的资源,如果你没有科学上网,这里很容易识别,如果你跟我一样使用windows的powershell,可以通过下面方式,对powershell命令行设置代理:
$env:http_proxy="http://127.0.0.1:1080"
$env:https_proxy="http://127.0.0.1:1080"
完成打包后,安装到windows中,双击运行,得到本文首图的效果
清明节3天假期到啦,又可以长时间编自己的程序了,搓搓手,趁着假期,将RPA软件的基本的Demo写出来吧,Enjoy Code!下篇文章见。