背景
公司需要制作一个桌面程序,之前有一个python写的cli工具,需要封装一个GUI跨平台使用,技术采用Electron + Vue技术栈,模板使用electron-vue,特殊之处是应用中使用pyinstaller打包成跨平台应用使用node调用。
基础项目中遇到的问题
上面提到该项目是根据electron-vue模板创建,在创建时,选用了electron-builder作为打包工具,打包后会在build目录下生成*-unpacked目录和对应的.exe、.deb等可安装文件。下面说下基于该模板开发时遇到的问题。
升级electron版本
- 模板使用的
electron版本是2.X版本,版本较低,故升级到了11.3.0版本,以方便使用最新的框架特性 - 升级后模板自带的
vuex-electron插件不好使故去掉了
系统中的路径分隔符的问题
- 由于历史原因路径的分割符在
Windows和POSIX不同\反斜杠,在Windows,如C:\tmp\test.txt/斜杆,在POSIX,如/tmp/test.txt
- 各个编程语言均有获取当前系统分隔符方法的api,
node中可以使用:const sep = require('path').sep - 故在手动添加路径的地方需要通过上面提到的
api获取路径分割符,动态进行拼接:import path from 'path' const downloadPath = `${this.$electron.remote.app.getPath('downloads')}${path.sep}合并表格.xlsx`
关于使用electron-builder打包遇到的问题
- 据说使用
electron-builder可以打包出跨操作系统的应用程序,保险起见,在Windows和Deepin系统分别进行打包 - 打包的配置基本通过
package.json即可配置完成:"build": { "productName": "excel-util", "appId": "com.excelUtil.xxx", "directories": { "output": "build" }, "files": [ "dist/electron/**/*" ], "dmg": { "contents": [ { "x": 410, "y": 150, "type": "link", "path": "/Applications" }, { "x": 130, "y": 150, "type": "file" } ] }, "mac": { "icon": "build/icons/icon.icns" }, "win": { "icon": "build/icons/icon.ico" }, "nsis": { "shortcutName": "ExcelUtil", "oneClick": false, "allowToChangeInstallationDirectory": true, "include": "./installer.nsh" }, "asar": false, "linux": { "icon": "build/icons", "target": "deb" } } - 以上是
electron-builder中关于打包的所有配置,下面拆分一下Windows打包配置,指定图标路径和nsis配置Windows应用安装和卸载的行为"win": { "icon": "build/icons/icon.ico" }, "nsis": { "shortcutName": "ExcelUtil", "oneClick": false, "allowToChangeInstallationDirectory": true, "include": "./installer.nsh" }Linux打包配置,指定图标和打包目标格式等"linux": { "icon": "build/icons", "target": "deb" }
- 打包使用的依赖包的镜像问题
- 打包会依赖很多外部的安装包,默认都是通过
github或者npm官方仓库,会有下载缓慢或者下载失败的问题 - 需要增加
.yarnrc配置文件在根目录下,配置下载涉及到依赖的的仓库地址registry "https://registry.npm.taobao.org" sass_binary_site "https://npm.taobao.org/mirrors/node-sass/" phantomjs_cdnurl "http://cnpmjs.org/downloads" electron_mirror "https://npm.taobao.org/mirrors/electron/" sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/" profiler_binary_host_mirror "https://npm.taobao.org/mirrors/node-inspector/" chromedriver_cdnurl "https://cdn.npm.taobao.org/dist/chromedriver"
- 打包会依赖很多外部的安装包,默认都是通过
开发中遇到的问题
主进程和渲染进程
electron本质是一个nodejs runtime和一个开源的chromium浏览器,开发时分为主进程和渲染进程- 主进程的代码在
src/main目录下 - 渲染进程的代码在
src/renderer目录下 - 同时会通过
webpack分别对main和renderer进行打包构建 - 进程间通信可以使用
ipcMain和ipcRenderer中的事件进行通信,以达到渲染进程可以调用主进程中的原生资源
主进程窗口配置
-
由于本次开发涉及的主进程开发相对简单,只涉及到一个
src/main/index.js,那就直接拿文件中BrowserWindow配置代码说事儿吧new BrowserWindow({ height: 563, useContentSize: true, width: 1000, fullscreen: false, // 是否允许全屏 fullscreenable: false, titleBarStyle: 'hidden', maximizable: false, webPreferences: { nodeIntegration: true, // 是否引入node api 这样渲染进程就可以i使用node api了 nodeIntegrationInWorker: true, enableRemoteModule: true // 是否引入remote模块 这样渲染进程就可以使用remote模块api了 } }) -
当然其中还涉及到引用本地数据库
lowdb,这里是参考掘友的文章 -
本应用还涉及到了单窗口实现,使用的是
requestSingleInstanceLock api来获取是否有窗口正在运行const gotTheLock = app.requestSingleInstanceLock() // 获取单体实例锁,设置应用单开 if (!gotTheLock) { app.quit() } else { app.on('second-instance', (event, commandLine, workingDirectory) => { // 当运行第二个实例时,将会聚焦到mainWindow这个窗口 if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() } }) app.on('ready', createWindow) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', () => { if (mainWindow === null) { createWindow() } }) }
调用python应用程序时遇到的问题
- 首先需要通过
pyinstaller分别打包出各操作系统环境中使用的cli应用程序 - 将打包后的
cli工具未压缩的包放入static/cli目录下 - 调用时即可通过
webpack吐出的全局__static变量获取对应的路径供node子进程调用 - 这里需要对打包配置进行修改才能达到打包后仍然可以正确调用
cli-
package.json的build字段中增加"asar": false,来保证打包后的应用不被压缩,node仍可以正常访问cli资源 -
对于
Windows来说,通过配置nsis的默认安装路径来保证路径可用,因为默认安装到C:\\Program Files下,有空格的路径会造成调用不通,故需要增加一下配置"nsis": { "some": "other config", "include": "./installer.nsh" }在
package.json中增加一个nsis安装脚本installer.nsh,让应用默认安装到C:\excel-utils目录下!macro preInit SetRegView 64 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\excel-utils" WriteRegExpandStr HkCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\excel-utils" SetRegView 32 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\excel-utils" WriteRegExpandStr HkCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\excel-utils" !macroend -
对于
Deepin系统来说,由于在webpack打包复制static/cli目录时会丢失cli程序的可执行权限,导致打包后命令不能执行,故增加一个增加权限的脚本和命令package.json中"scripts": { "build:linux": "node .electron-vue/build.js && .electron-vue/changeMainMod.sh && electron-builder", }.electron-vue中增加changeMainMod.sh,并使其在业务代码打包完成后调用#!/bin/bash for main in `ls ./dist/electron/static/cli/*/main` do echo "正在设置$main为可执行权限" chmod +x $main done
-
webpack.renderer.config白名单的问题- 由于使用的
vue2.X,UI库使用的则是element-ui,开发时发现使用el-table组件不正确显示,查阅资料后发现需要增加到打包白名单里面,注释写的也很详细,需要指定UI libraries来让编译.vue文件
/** * List of node_modules to include in webpack bundle * * Required for specific packages like Vue UI libraries * that provide pure *.vue files that need compiling * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals */ let whiteListedModules = ['vue', 'element-ui']- 由于使用的
番外
技术选型时有两个选择一个是Electron,一个是NW.js,这里有一个文档来说明二者的异同,主要区别就是:
Electron有很多成功案例Atom、VScode等等耳熟能详的大众应用,当然NW.js也有钉钉和微信开发者工具这样的巨头应用- 总体来说
Electron有更多的用户群,更多三方工具包,更活跃的社区,有问题可以很快找到解决方案 NW.js保持了对Windows xp系统的支持,而Electron并出于安全性考虑并不支持- 再就是对
node的集成,Electron集成了libuv在两个进程中,均可以直接使用,避免了对Chromium的hack,而NW.js则需要通过派发的方式使用