Electron 是一个跨平台应用框架,原本为 Atom 设计,后来单独分离了出来。它提供了一些与原生系统交互的 API ,可以方便快速使用 HTML、JavaScript、CSS 等 Web 技术开发跨平台应用。
最近基于 Electron 开发了一个简单的 理财计算器 。发布了 1.0 版本之后,总结一下这段时间的开发经历。哦对了,价格是随便定的,主要是熟悉下流程,大家千万别买。
这篇文章只包含个人开发小结,不包括各种框架工具的基础使用,如有需要请自行 Google 。
理财计算器的功能很简单,目前只做了股票的保本卖出价的计算。
输入买入价、买入数量等信息,算上各种手续费之后,输出保本卖出的价格。
看起来很简单的功能,大概却做了5天,主要在开发环境配置上卡了挺久。
一开始需要做的是搭建整个项目的基础框架,主要包含以下几个方面:
上面这一套东西要搞一下还是比较费时间的,好在网上有各种现成的模板,货比三家之后最后选用了 electron-boilerplate-vue ,搭建好后文件目录如下:
. ├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── app │ ├── ... │ ├── main.html │ ├── main.js │ └── package.json ├── build │ ├── ... │ ├── webpack.base.conf.js │ ├── webpack.dev-background.conf.js │ ├── webpack.dev-server.conf.js │ └── webpack.pro-build.conf.js ├── config.js ├── package.json └── test
接下来就是前端页面部分。一直想试试 Material Design 风格,刚好这次是个不错的机会,于是就开始找找有没有 MD 方面的前端库。
先是想看看有没有类似于 Ant Design 这样的成套 vue 组件库,于是找到了 material-ui ,可惜是 React 版本的。找找了 vue 版本的似乎只有 vue-mdl 和 vue-material 这两个不太成气候的库,仔细想想本身它们都依赖于其他前端库和 vue ,万一更新不及时岂不是很坑爹。于是弃了这个想法。
后来就是去找类似于 semantic-ui 和 Foundation 这样纯粹的前端库,找到 materializecss 这个库,页面简洁大方,破儿费(Perfect)啊!
然后,就是为期两天的填坑之旅。
是的,一共5天的开发时间,调教这个前端库花了2天,而且坑爹的是,最后还没用上。主要问题在于它的 JS 代码是基于 jQuery 的,而很多插件又是在 $(document).ready
里加载的,对 SPA 非常不友好,因为 document ready 的时候我的 dom 们还没 ready 。再加上我对 Webpack 又不是很熟悉,导致点击事件不响应,不知道是 webpack 打包的问题还是 vue-loader 加载顺序的问题还是 babel-loader 编译的问题还是它自己库的问题还是 jquery 引入的问题。
总之就是,一套系统过于庞大,虽然脚手架让我避开了大量的体力劳动,但是同时也引入了很多我不熟悉的未知因素。于是就刚好花两天时间,仔细学习了项目中用到的各种技术和工具。熟悉了之后才发现我以前多虑了,问题肯定出在源码上。然后就找到了问题的原因。
最后决定放弃 materializecss 这个库,然后去试了试 Google 家的 Material Design Lite 。
万万没想到,5分钟解决战斗。
引入 CSS 的方法和 materializecss 类似,都是在 SCSS 里 import
它的原文件:
/* IMPORT VARIABLES */ @import 'material-design-lite/src/_variables.scss'; /* OVERRIDE VARIABLES */ $layout-screen-size-threshold: 0px; /* IMPORT MDL */ @import 'material-design-lite/src/material-design-lite.scss';
引入 JS 文件的方法也很简单,和其他库一样 import
就行:
import 'material-design-lite'
不过和 materializecss 不同的是,文档里在 《 Use MDL on dynamic websites 》 写清楚了动态更新页面后的更新方法:
// upgrade element componentHandler.upgradeElement(button) // upgrade dom componentHandler.upgradeDom()
于是在 vue 的项目里,只要这么写就可以了:
<template> .. </template> <script type="text/babel"> export default { ready: () => { componentHandler.upgradeDom() }, components: { ... }, } </script> <style lang="scss" rel="stylesheet/scss"> </style>
谷歌爸爸!受我一拜!
接下来就是业务部分,计算器嘛还是挺简单的,感觉最有趣的部分是实时更新计算结果,这个功能通过 vue 这种数据绑定框架来实现真是太合适了。
核心代码是这样的,先用 v-for
组装表单控件:
<div> <div class="mdl-tabs mdl-js-tabs"> <div class="mdl-tabs__tab-bar"> <a v-on:click.prevent="clickTab(0)">沪市 A 股</a> <a v-on:click.prevent="clickTab(1)">沪市 B 股</a> <a v-on:click.prevent="clickTab(2)">深市 A 股</a> <a v-on:click.prevent="clickTab(3)">深市 B 股</a> </div> <div> <div v-for="item of currentTabItem"> <div> <input type="text" id="{{ item.id }}" v-model="item.model"/> <label for="{{ item.id }}">{{ item.label }}</label> <span>{{ item.suffix }}</span> </div> </div> <div id="result-panel" class="mdl-color-text--primary"> {{ result }} </div> </div> </div> </div>
然后在 JS 里绑定数据:
export default{ ready: function (){ componentHandler.upgradeDom() }, methods: { clickTab: function (index){ this.currentTab = index this.$nextTick(() => { componentHandler.upgradeDom() }) }, }, data: function (){ return { currentTab: 0, items: items, } }, computed: { result: function (){ ... return `${part1}${part2}` }, currentTabItem: function (){ return items[this.currentTab] }, }, }
这样一旦输入的数字发生改变, model 就会改变,就会触发 result 改变,从而刷新 dom 上的显示结果。
其他杂七杂八的东西比较多,拎一些踩过的坑记录一下。
Electron 虽然提供了简单快捷的跨平台开发方案,但是并没有简单快捷的跨平台打包方案。从 《 Electron application packaging 》 来看,打包过程比较繁琐,反正我是划了5次触摸板才滚到了底部。
所幸的是, electron-packager 帮我们解决了这个问题,这个工具已经包含在了前面的了 electron-boilerplate-vue 里,现在打包只需以下命令即可:
"package": "node build/package.js", "package:osx": "node build/package.js --platform=darwin", "package:mas": "node build/package.js --platform=mas", "package:win": "node build/package.js --platform=win32", "package:linux": "node build/package.js --platform=linux", "release": "npm run build && npm run package",
签名也很容易,只要加上 osx-sign
的选项即可:
if (packagerConfig.platform === 'mas') { Object.assign(packagerConfig, { 'app-bundle-id': appManifest.bundleId, 'osx-sign': true, }) }
打包之后的结果是 .app
文件,如果需要上传到 Mac App Store 可以用 electron-osx-flat 将其转化成 .pkg
文件,然后通过 Xcode 里的 Application Loader 上传。
然而,并没那么省心。。。打包之后提交审核,审核人员说你这应用打开白屏了。我自己打开一看还真是。。。感觉是 osx-sign 导致的问题,于是就不打 mas 的包了,还是通过手动写脚本的方式签名,参照 Mac App Store Submission Guide 即可。
图标问题也卡了几个小时,主要是因为提交到 iTunes 之后提示说,需要提供 512 和 512@2 的 icns 文件,而打包结果里没找着。
这就奇怪了,我明明放进去了为什么它找不到呢?后来我仔细看了下我的 icns 文件发现是 png 转 icns 的时候出了问题。
在我使用 iconvert icons 的时候,导出的图片没有 1024*1024 的图:
而在我使用 cloudconvert 的时候,则只有 1024*1024 的图:
嗯?!
这不是坑爹吗!
于是只好手动撸,刚好电脑里有以前做 iOS Icon 时装的 prepo ,可以将 1024 的 png 导出各个尺寸的图片并且放到 iconset 里:
然后只要用一行自带的命令就能将它们打包成 icns 文件:
iconutil -c icns icon.iconset
大功告成。
因为最近忙着搬家的事情,这篇小结就暂时写到这里。
总的来看, Webpack+Vue+MDL+Electron 开发跨平台应用是一种很不错的开发体验。
虽然一路踩了很多坑,但是在应用上架的那个时刻。
我只想说。
Excited!