前言
这篇文章不是 Electron 的教程, 而是作者在去年做的一个大型项目的总结, 我会从立项到技术选型到开发一一道来, 如果大家能在看作者自己给自己挖坑的过程中有所收获, 那就再好不过了.
使用技术
这里先罗列一下用到的主要技术, 其中也有一部分在发现问题后放弃的
Electron5 - Electron7(没错, 从开始立项时的 Electron5 的版本, 到最后的 Electron7, 鬼知道经历了什么)
- sqlite3
- sequelize(ORM 框架, 搭配数据库使用可以不用写 sql 语句)
- vue-cli-plugin-electron-builder(使用这个插件来整合 vue-cli 和 electron)
- ant-design-vue
- element-ui
PDF.JS
- electron-edge-JS(使用这个包来调用 dll)
- log4js(用来记 log)
- amqplib(rabbitmq 的 node 实现)
- grpc
项目背景
因为是公司的商业产品, 具体名字就不写了, 这个系统包括服务端, PC 客户端和 iPad 端. 这次的 Electron 项目是对该产品的 PC 客户端的重构, 原客户端是用 WPF 技术完成的, 因为公司的战略需要将以前老的 WPF 技术写的所有软件用 Electorn 重构, 所以就有了这个项目. 开发人员加上作者一共有三位, 只有作者是前端, 其余两位都是. net 工程师, 负责维护原项目的, 开始现学 Electron. 作者以前也没完整开发过 Electron 项目, 就在这样的情况下, 项目启动了.
项目功能
因为公司的这个产品是针对酒店行业的, 所以这个 PC 客户端的主要功能包括对酒店客人入住, 离店, 消费单据的签字, 打印, 包括支付以及对接服务端对客人住店对处理等等.
前期调研
因为只有作者一个人是前端, 所以建项目这个事情只能交给本人来做了.
前端开发的三大框架当然得用上, 虽然我挺想用 React 的, 但是公司的技术栈是 Vue, 考虑到成本当然选择了 Vue.
当时立项的时候 Electron 稳定版更新到了 5.x.x 版本, 所以直接使用了 Electron5.
接下了就是就是结合 Vue 和 Electron, 公司以前有一个老项目用的是 vue-electron, 但是这个库的作者已经好久没更新了, 而且集成的还是 vue-cli 2 的版本, 所以综合考虑了一下, 没有使用以前的方案. 采用了 vue-cli-plugin-electron-builder 这个 vue 插件来整合 vue 和 electron, 这里推荐大家看下这篇文章手把手教你使用 Electron5+vue-cli3 开发跨平台桌面应用, 讲的很详细.
接下来是数据库的选择, 其实这个项目对数据库对要求很少, 基本上就是存一下配置文件和最多几百个 PDF 文件, 当时也考虑过直接用文件形式存储, 但在和老大聊过之后, 谈到了以后功能的扩展问题, 还是决定使用数据库. 然后是在像 sqlite3 这样需要 build 的 native 库, 还是一些不需要 build 进 electron 的 JSON 格式的轻量数据库之间进行选择. 还是考虑到用户可能有比较多的 PDF 的数据的情况下, 选择了 SQLite. 然后又在老大的要求下加入了 ORM 框架 Sequelize 来配合 sqlite3 使用.
我们开发组很早就想要引进 TypeScript, 加上组内有很多写 C# 的. net 工程师, 学习成本还是比较低的, 虽然 Vue 用 TS 写有些别扭, 但是我还是趁这次机会和老大提出了引入 TS 的想法. 结果老大还是想要等到用 TS 重写的 Vue3.0 发布后再引入 (已经 2020 年了 vue3.0 还没有发布), 所以 TS 没能用到项目上来.
正式开发
项目结构
整个项目的目录结构基本上是按照 vue 的结构来建的, 但是在开发到后期发现存在一些问题, 关于项目目录如何建这个问题我会单独再写一篇文章来分享自己的看法, 这里就暂且不提了.
Native 编译问题
使用 Electron 开发之后, 我发现问题最难解决的就是 Native 包的编译问题, 凡是需要调用其他语言写的工具都有编译这一步, 本项目需要编译的当然是 Sqlite3 了.
走过了数不尽的弯路之后, 我先来提出最后的成功解决方案, 在 NPM 的后置钩子 postinstall 中运行这条命令 electron-builder install-App-deps, 这条命令的意思就是安装编译相关的依赖.
先来讲讲第一个坑, 其实在使用 vue-cli-plugin-electron-builder 生成项目后, 这个命令是默认就在 postinsatll 里面的, 但是我一开始没有编译成功的原因就是因为我的 electron 不是从 NPM 官方源下载的, 而是从公司自己的 NPM 源, 公司的 NPM 源是连接的淘宝源结果造成了这个问题. 所以无论下任何 NPM 包一定一定要从 NPM 官方源下载, 有些资源太慢就挂代理 (这个是程序员必备技能了吧), 即使是用淘宝源也千万不要用 cnpm 命令, cnpm 真的是坑太多了.
还是 splite3 的问题, 我在 Mac 上可以跑起来了. 但是当放到 windows10 上就又出问题了, 看报错是 node-gyp 在编译过程中出现的问题. 最后发现在 Windows 上使用 sqlite3 和 electron 得配置以下环境.
- python2.7(有的同事电脑安装 python3.5 会报错)
- Windows-build-tools(这个非常非常重要, 如果你的电脑安装了 VS 的话, 可能不需要这个东西, 但是如果没有安装 VS, 务必安装这个包, 它会配置 node-gyp 在编译 c++ 等语言的代码时必须的环境, 非常重要)
数据库问题
sqlite3 官方的 API 不太好用, 所以我封装了一套 Promise 格式的 API, 需要的可以参考我以前的文章 Electron 中 sqlite3 的安装以及 Promise 形式 API 的封装.
在这个项目中我们使用了 Sequelize, 这是一个 ORM 框架, 配合 sqlite3 使用可以让我们不用写 sql 语句, 而是通过对象的形式来使用数据库, 但是当配合 vue 脚手架使用的时候会出现问题, 控制台又报了 sqlite3 的错误. 这个问题是关于数据库这块我处理时间最长的一个, 最后借助 Goole 搜索引擎, 在某个外国作者的个人博客上找到了解决方法, 在 vue.config.JS 里这样配置
- configurewebpack: {
- externals: {
- sequelize: "require('sequelize')",
- sqlite3: "require('sqlite3')"
- }
- }
如果是直接用的 webpack, 把 externals 属性配置到配置文件里就可以了.
UI 框架的选择
一开始这个项目使用的是 ant-design-vue, 在 UI 基本完成之后的某一天, 我和一个做混合开发也在用 ant-design-vue 的同事互相吐槽了一下这个 UI 库里面 table 组件 (确实操作反人类啊). 没想到这事被旁边的老大关注了, 在经过 8 个人的 UI 库选型讨论大会之后, 最终决定使用 element-ui, 而且没有彻底完成的项目都要使用 element-ui 重写. 我....... 心里苦啊.
其实就我作为前端对角度而言, 使用 element-ui 没有问题, 但是重写现有的项目 UI 就真的没有必要了, 毕竟我们前端使用这些库的组件的时候都是按需引入的, 而且其实很多时候都需要前端来调整组件的样式. 老大可能是从前端组整体的角度来看希望能使用统一的 UI 库, 总之, 我之后又把所有 ant-design-vue 的组件全部替换成 element-ui 的组件.
PDF 的展示
我们这个项目最主要的功能之一就是对各种单据进行签字, 所以如何展示单据并签字就要用到 PDF.JS 了, 这部分功能是由另一位同事调研了半个月之后负责开发的, 这位同事是. net 开发, 对前端不太熟悉, 最后导致这部分的功能成了我们项目开发中最大的难点.
PDF.JS 是将 PDF 文档通过 canvas 显示在页面上, 然后我们再将应用窗口推到 wacom 的设备上让用户签字, 然后问题就产生了.
签字字迹不圆润, 有断点的存在.
PDF 展示模糊.
如何无损缩放.
笔迹和 PDF 合成后变模糊.
PDF 文档切换闪烁问题.
下面是填坑的过程:
参考了一些开源项目对笔迹的点的算法处理, 绘制出了比较圆润的曲线.
参考 PDF.JS 官方 Demo, 初始化时渲染高精度 PDF
使用 PDF.JS 读取高精度倍数的 PDF 绘制到 canvas 上, 而字迹我则是通过 Path2D 来记录绘制轨迹然后重绘来实现无损的缩放.
我们是通过公司同事使用 C# 来写的一个 dll 来将 PDF 和字迹的 canvas 合成的, 这个最后通过给这个 dll 传高精度的 PDF 和字迹实现比较清晰的合成.
参考 PDF.JS 官方 Demo, 使用两个 canvas 交替显示, 消除闪烁现象.
虽然问题看似解决了, 但最后我们发现实际效果还是差强人意, 在最终有调整了半个月之后, 老大看了我们的最终演示, 最后决定签字部分直接使用原来的 exe 程序来实现, 我们的应用仅展示 PDF 文件.
虽然这部分工作并不是我负责的, 但是我也参与其中改了不少东西, 所以到最后换方案还是比较难受的, 就好像自己考试考砸了一样.
打印
PDF 的打印真的是让人奔溃, 因为我们项目对打印对要求还是比较高的, 比如说可以配置打印机的纸张来源, 纸张大小, 指定打印机静默打印等等. 而 electron 的打印 API 不能完全实现这些功能. 所以一开始这个打印功能到底是要在 electron 上用 JS 做还是调用 c# 写的 dll 或 exe, 我就进行过研究, 因为这是个重构项目, 这部分功能有以前写好的 c# 代码可以用, 调用 dll 应该是比较方便的. 但是.., 老大想要使用 electron 自带的 API, 他本人尽量想要少使用 dll, 哪怕功能得削减.
既然这样, 那就决定用 electron 的打印 API 了, 但是刚成功打出两张之后就发现了一个问题. electron 的打印 API 有个静默打印的设置, 设置后不会弹出系统的打印对话框, 但是如果我开了这个设置, 那么指定的打印机就会失效, 系统会使用默认的打印机打印.
又是 Google 找问题时间, 最后我到 electron 的 GitHub 主页上看到了别人提的 issue, 作者回复这是个 bug, 而且由于底层代码问题只能在 electron7 中才能修复. 心累啊, 我建项目的时候才 electron5, 现在就要用 electron7 了? 但是没办法, 这个时候的稳定版是 electron6, 我只好在 NPM 下了 electron7.0.0-beta.3, 这是我用的最长的一个 NPM 版本.
这之后还有不少问题, 比如说打印出来多了页边距, 获取不到实际打印结果, 打印次数多直接奔溃之类的. 最后虽然磕磕绊绊的完成了打印的功能, 但是如果有人想要在 electron 上打印的话, 如果需求比较复杂, 强烈建议不要用 electron 的打印 API, 尽早换别的方案, 这个坑就不要再踩了.
错误日志记录
这部分用了 log4.JS 这个库, 这个功能还是比较顺利的, 照着官方文档基本上没有什么坑.
与 iPad 通信
我们一般是将文档推到 wacom 上让客人签字, 不过也有些客户想用 iPad, 所以后来就开发了一个 iOS 应用, 用来接收 pc 端推过去的 PDF 文档, 然后在 iPad 上签字, 完成后在推送到 pc 端.
这个功能在老项目上是用 rabbitmq 来通信的, 使用 electron 重构的新项目我本来是想要看看能不能使用 grpc 来实现, 结果在大概看了官方文档也用 node 写了个简单 demo 之后, 发现在 electron 上不能使用, 因为会编译失败. 在查阅来一些资料后, 我认为是 electron 版本太新了, 但是因为低版本的打印 API 有 bug, 又不能降版本, 所以只好改用 rabbitmq, 和老版本保持一致的方案. rabbitmq 的 node 实现是 amqplib, 使用 amqplib 顺利完成了这个功能.
小结
项目暂且就记录到这里吧, 总而言之, 在这个项目的开发中我学到了很多, 也遇到了很多问题. 看到这篇文章的人如果对于我踩的坑有更好的处理方式, 欢迎留言指教.
来源: http://www.jianshu.com/p/e55e67a65191