我的第一个Electron应用
前一阵突发奇想,想写一个汇总所有骑行数据的网页。又想到最近看了下Electron,干脆写一个能够生产这样网页的工具,造福自己,造福他人。
Electron是啥
Electron一套由Github开发的开源库,基于Chromium和Node.js,支持通过HTML、JavaScript、CSS来构建跨平台(Mac、Windows、Linux)应用。Electron起初是为文本编辑器Atom而开发的一套开发框架,名为Atom Shell。现如今已经为众多应用所用。
从开发者角度看,Electron分为main
和renderer
两部分。前者运行在主进程中,以创建web页面的方式显示一个GUI;后者是渲染进程,每个Electron的web页面运行在其中。通常的浏览器内,网页通常运行在一个沙盒的环境不能够进行原生操作。 在Electron中,可以在渲染进程使用Node.js的API和Electron提供的众多API(GUI相关除外),和操作系统进行一些低级别的交互。主进程和渲染进程通过ipcMain
和ipcRenderer
相互沟通;也可以通过remote
的方式发起,后者要更简洁些。
在项目结构上,官网并没有限制,electron-webpack的project-structure可以参考,安全性上,可以参考官网的介绍。要想获得对Electron概念的快速认识,可以看看关于Electron和快速入门,再去知乎Electron精华话题看看,或者看看awesome list也是极好的。
调研准备
地图考虑还是使用百度地图API(因为上个小项目用的就是百度地图,好上手),根据demo演示来看,根据数据点画个折线是没什么问题的。地图的part没问题了。
下面就是数据的part。去确认了下我骑行常用的APP行者,网页和APP都有导出功能。导出格式为.gpx
的gps数据文件。OK,数据的来源也有了。
至于怎么把这些点连线搁在地图上,就是我要干的活了。
功能设计
但是事情没那么简单,既然选择Electron来练手,光做个展示的网页出来就很没意思了。这也不是Electron的用武之地。于是能够想到的就是,做一个可以生成上面那个网页的工具,一方面减轻我的负担,让我在日后维护时省心省力;另一方面也能造福他人嘛。
现在整理一下,我拿在手里的是一堆.gpx
的文件,产出是可以画图的网页。稍微分解一下:
- 网页是需要独立存在,不需要用户配置的,这些gps数据必须单独存储,可以使用前端友好的JSON文件。这个转译过程需要在Electron应用中完成
- 网页需要能够配置生成,不需要用户手写,因此在应用里需要填充HTML模板,生产HTML文件。
- 页面并不复杂,不需要使用Vue、React甚至webpack的加持,作为我的第一个Electron应用,把握整体感受要紧
开写
相关环境
安装Electron过程中,报错node版本过低。只能重新安转新版本的node,windows下只有nvm-windows这个选择。安装完成后,之前全局安装的npm包只能重头再安一遍。先安装nrm再说。
注意:安装nvm-windows前,强烈建议卸载已有的Node.js
boilerplate
boilerplate即骨架。现在前端的环境里,一门成熟的开源库是一定有一堆boilerplate的,毕竟程序猿懒人多。Electron也不能免俗。可以从awesome list中挑一个看上眼的。如果项目比较大,可以直接用electron-vue这种省心省力,一键式配置,开发打包工具一应齐全。这里我从官网提到的quick start,除了一些样例代码,啥都没有,正合我意。
(我曾经尝试使用electron-webpack-quick-start,想着顺便打包了electron-builder,还有webpack、热加载,岂不美哉。不过根据这里所说,是没有index.html暴露给你的,这几乎就意味着必须要用Vue、React这样的解决方案,但是electron-webpack这个库并没有包括,需要自己add-on,但是按照文档所说的操作后,并不能识别.vue
文件,而且也没有vue-devtool。这是坑之一)
转译
转译过程是在renderer.js
中完成的。实际上,项目里大多数业务逻辑也是在渲染进程中完成的。核心在把gpx文件里的信息解析出来,除了<desc></desc>
中的meta信息之外,其余的点结构大致像下面这样;
1 | <trkpt lat="40.106419" lon="116.369812"> |
幸运的是,npm上早就有gpx的parser。gpx-parse的功能足够满足我们需要了。
1 | var gpxParse = require("gpx-parse"); |
顺带写几个input框(包括<input type="file">
),测试一下,没啥问题(排除掉中间处理yarn和npm冲突问题的话)。观察一下,返回值是一个GpxResult
类型,里面有metadata
,routes
,tracks
等字段,只有tracks中记录着点的信息。考虑到tracks
和里面segments
字段是数组的情况,要进行一下flatten的处理。最后,整个转译过程大概如下:
1 | function serialize(file, index) { |
写一个示例网页
既然最后的网页是生成出来的,就有第一个能够成功工作起来的网页作为模子,好抽离范本出来。先搭一个最简单的HTML架子,插入百度地图的script标签。
1 |
|
下面我们把工作稍微分析一下:
- 从本地读取JSON文件,意味着自己实现一个ajax,考虑兼容性(毕竟没了babel和webpack),使用XMLHttpRequest
- 读取当然得是异步的,JSON文件很有可能很多,需要依次进行
- 地图配置和画图就很简单了,参考API就行了
第一个工作不难:
1 | function getJSON(path, callback) { |
第二个工作也不难,在递归函数的外面设置控制递归的变量就可以了。
1 | var pool = Array.apply(null, Array(length)).map(function(v, i){return i+'.json';}); |
OK,大功告成(排除其余逻辑bug之后),赶紧接上renderer.js
那边转译好的JSON文件看看骑行轨迹吧!
你以为事情会这么简单么?
当然不。
坐标换算
图是出来了,但是路线有偏差,发现明显有所平移。这是怎么回事,搜索过后才发现,百度所采用的坐标并不是gps数据中的真实大地坐标,而是在火星坐标基础上再次加密的百度坐标(更多)。官网示例上也给出了gps坐标转成百度坐标的API。
得,那就在转译成JSON数据前多map一段呗。仔细一看,Convertor
的介绍里赫然写着“一次最多10个点”,居然还限流(其实不只是限流的问题,递归的写法也要变化)。一条路线至少上千个点呢,算了先试试看速度吧。
两条路线用了30s才显示出来,果然很慢……
只能自己实现转译过程了,网上倒是有一些例子,都差不多。尝试了一下,发现有点效果,但是路线还是有偏移。试了半个多小时后,总算找到了一个完美的JS转译代码,感谢原作者。
1 | function delta(lat, lon) { |
这转译过程,要不是有先行者,我怕是要倒在这里了。
HTML模板
示例HTML已经工作起来了,现在就是抽出模子的过程。网页并不复杂,可以用简单的HTML template解决问题。John Resig的方案如下:
1 | // Simple JavaScript Templating |
看上去很眼熟,以前的项目似乎见到过。
把之前的示例HTML放在index.html
的<script type="text/template"></script>
中,在渲染进程里加上代码看看?
1 | console.log(tmpl('template')); |
嗯……报错了。提示“Missing ')' after argument list
”。加断点调试发现是标签里的"
打断了new Function
的语句。尝试了多种方法无效后,索性使用encodeURIComponent
想处理掉麻烦的特殊符号,但是这么做之后就无法匹配<%=%>
了。
于是最后选择underscore的template方法。再试试……
没问题了。之后把允许用户填写的部分抽出来,就可以把index.html
的生成放在转译代码身旁了。
1 | const template = document.getElementById('template').innerHTML |
再次运行,测试生成的网页能否工作?答案当然是可以。
苦力活
技术上的问题解决了,现在从用户填写信息到最后生成能用的展示页面也没有问题了。初版下面的问题就是美化了。
- CSS修饰样式
- 将模板HTML文件压缩(包括JS和CSS),因为反正用户不会修改内容,不需要考虑可读性
- 一些保护性编程和边缘情况兜底
最后测试结果如下:
生成效果如上。
发布
初版开发已经完成了,只剩发布出来给别人用了,考虑到官网文档讲得实在不清不楚,不如用一个好工具帮我们完成。
这里使用electron-builder。跟着介绍里一步步完善package.json
和项目结构。加上依赖后,执行yarn dist
生成可分发的文件。
嗯……果然失败了。原因很简单,网络错误,Electron镜像文件下载失败。还好淘宝有electron镜像。通过设置ELECTRON_MIRROR环境变量,可以大大加快速度。
1 | export ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ |
然后,再次执行yarn dist
,在从Github下载其他相关文件的时候,仍然会网络错误。于是我机智的从网页上下载下来,直接放在了目标目录下。再次执行任务,居然不能识别出来。好吧……
故事的最后,打包还是完成了。不过由于生成文件的目录写成了相对目录,生成的文件得通过搜索才能找到,考虑后面生成在桌面。
–END–