前言
前段時間,我用electron-vue開發了一款跨平臺(目前支援Mac和Windows)的免費開源的圖床上傳應用——PicGo,在開發過程中踩了不少的坑,不僅來自應用的業務邏輯本身,也來自electron本身。在開發這個應用過程中,我學了不少的東西。因為我也是從0開始學習electron,所以很多經歷應該也能給初學、想學electron開發的同學們一些啟發和指示。故而寫一份Electron的開發實戰經歷,用最貼近實際工程專案開發的角度來闡述。希望能幫助到大家。
預計將會從幾篇系列文章或方面來展開:
- electron-vue入門
- Main程式和Renderer程式的簡單開發
- 引入基於Lodash的JSON database——lowdb
- 跨平臺的一些相容措施
- 通過CI釋出以及更新的方式
- ...(想到再寫)
說明
PicGo
是採用electron-vue
開發的,所以如果你會vue
,那麼跟著一起來學習將會比較快。如果你的技術棧是其他的諸如react
、angular
,那麼純按照本教程雖然在render端(可以理解為頁面)的構建可能學習到的東西不多,不過在main端(electron的主程式)應該還是能學習到相應的知識的。
如果之前的文章沒閱讀的朋友可以先從之前的文章跟著看。
LOGO的準備
經過前面幾篇文章的實戰,我相信大家已經對於構建一個基本的electron應用沒有太多的問題了。本文主要闡述一下如何讓我們的應用通過CI系統來自動幫我們構建應用,然後釋出給使用者使用。以及之後如果有更新,要如何通知使用者更新。
當然,在此之前,我們還需要做一件事:給你應用加上好看的LOGO。LOGO的設計和製作不在本文的設計範圍內。為了我們的應用能夠跨平臺地使用,不同平臺上應用的LOGO尺寸和格式也不盡相同。三個平臺所需的圖片格式如下:
- Linux - png
- macOS - icns
- Windows - ico
準備一張1024*1024以下,256*256以上(長寬一致)的png圖片,(推薦512 * 512)然後我們可以用一些工具來實現從png到其他兩種格式。搜尋png轉ico或者png轉icns的話有很多線上轉換的網站,可以去上面線上轉換。在mac上我推薦用的是image2icon這個工具。
然後我們將所得的三個圖片檔案,放到electron-vue專案根目錄的build/icons/
目錄下。
不同平臺的構建配置
本文我們主要採用electron-vue已經配置好的基於electron-builder的構建指令碼來進行我們的應用構建。構建指令碼會讀取package.json
裡的build
欄位裡的配置來進行構建。electron-vue預設的配置如下:
"build": {
"productName": "ElectronVue",
"appId": "org.simulatedgreg.electron-vue",
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"directories": {
"output": "build"
},
"files": [
"dist/electron",
"node_modules/",
"package.json"
],
"mac": {
"icon": "build/icons/icon.icns"
},
"win": {
"icon": "build/icons/icon.ico"
},
"linux": {
"icon": "build/icons"
}
}
複製程式碼
簡單講述一下build配置裡的一些欄位的含義。
首先productName
是你的應用的名字。appId
的作用是用於Windows平臺區分應用的標識。(注意該配置必須配置,而且稍後會有使用該配置的地方。如果不配置不使用的話,構建出來的Windows平臺的應用將無法傳送eletron的桌面通知)dmg
這個配置裡描述了macOS平臺裡,開啟dmg
安裝包後顯示的介面裡的資訊。如下圖:
表示了有兩個標識,一個是應用檔案,座標是(130, 150)
, 一個是應用資料夾的快捷方式,座標是(410, 150)
。
directories
的output
欄位是你應用打包完生成的檔案放置的目錄。
files
指明瞭要打包的目錄。
而mac
,win
,linux
是針對三個平臺的不同的配置了。可以看出預設的配置裡對它們的配置都是指向了不同的icon圖示(也就是上一節所說的LOGO)。
PicGo在實際開發中,針對一些情況對預設的build
配置項做出了一些增改:
"build": {
"productName": "PicGo",
"appId": "com.molunerfinn.picgo",
"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",
"extendInfo": {
"LSUIElement": 1
}
},
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons"
}
},
複製程式碼
由於PicGo在macOS上主要是一個頂部欄應用,所以在底部docker欄我並不想擁有一個佔位的圖示,所以在mac
欄位里加入了:
"extendInfo": {
"LSUIElement": 1
}
複製程式碼
這個屬性。參考相關issue。
在Windows平臺上,預設打包出來的安裝包並沒有辦法選擇安裝的路徑,只會預設裝到C盤的使用者目錄。這個並不是我們想要的。我們想要的是讓使用者自己選擇安裝的路徑。
所以需要修改windows
的一些配置以及加上一個nsis
的配置來實現:
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
複製程式碼
由於目前我還沒有打包過Linux平臺的應用,所以Linux相關的配置暫時先不做修改。
Windows平臺打包的一個小坑
還記得前面說到的一個配置:appId
麼,這個配置需要我們在主程式index.js
裡也要使用。否則打包後的應用將失去Windows平臺的應用通知功能。這個appId
是可以任意取的,只要保證不和其他應用重複即可。對於PicGo而言,appId
是com.molunerfinn.picgo
。
開啟你的main/index.js
,在Windows平臺的時候加上這個appId
:
// ...
import pkg from '../../package.json'
// ...
// ...
if (process.platform === 'win32') {
app.setAppUserModelId(pkg.build.appId)
}
複製程式碼
這樣就解決了通知的那個問題。
通過CI系統自動構建與釋出
版本相關
釋出應用其實是一個比較繁瑣的活,往往跟你的版本控制綁在一塊,所以通常在專案開始的階段就要有所佈局。我說說我的做法吧,不一定很科學,不過簡單易行。
- 倉庫主要兩個分支,一個dev一個master。平時在dev上開發,只有在釋出新版的時候merge到master上。
- 書寫簡單的更新版本的指令碼,一鍵打tag+push到GitHub。
- 結合CI系統,自動構建master分支的程式碼,並將應用推送到GitHub倉庫去。
其中簡單的更新版本的指令碼我是在package.json
裡寫了簡單的scripts
:
"scripts": {
"patch": "npm version patch && git push origin master && git push origin --tags", // 小版本
"minor": "npm version minor && git push origin master && git push origin --tags", // 次版本
"major": "npm version major && git push origin master && git push origin --tags" // 大版本
}
複製程式碼
裡面用到了npm的一個命令,npm version [options]
,具體可以參考version的文件。簡單來說,它能夠自動幫你升級版本,修改package.json
裡的version,並打上相應的git tag,很方便。
舉個例子,一個符合語義的版本號通常由如下三個部分組成:major.minor.patch
,比如1.5.3
。如果我執行了npm run patch
,那麼將會將小版本更新:1.5.4
,同時修改package.json
裡的version
欄位為1.5.4
並自動打上一個git tag 1.5.4
,並將這個修改和tag推送到遠端。
不過需要注意的是,一開始我是通過electron-vue自帶的npm run build
這個指令碼讓CI去執行構建,但是發現無法自動上傳到GitHub的release裡。所以通過查閱相關資料後,發現最簡單的就是把對應的npm scripts命名為release
。於是我把原本的npm run build
的指令碼複製了一遍,起了一個新名release
:
"scripts": {
// ...
"release": "node .electron-vue/build.js && electron-builder",
// ...
}
複製程式碼
CI相關
說到這裡都還沒說到CI系統。什麼是CI?可以參考阮一峰老師給出的解釋《持續整合是什麼?》。我們如果每次釋出應用都需要我們在本地構建,然後手動上傳到GitHub(或者其他地方)去,然後讓別人能下載的話,未免太累了。而且通常我們開發electron應用就是為了能夠跨平臺,但是要構建不同的平臺的應用意味著我們要在不同的平臺分別構建。這也是不能忍受的。
於是網上有一些第三方的CI系統,它們能夠幫我們,在某些分支(比如master)發生了某些更新(比如更新了tag)的時候幫我們執行某些指令碼(比如構建、測試)。這樣就省卻了我們在本地、多平臺構建的煩心事,而且讓一些都變得「自動化」了起來。
有了CI之後,我的electron應用的釋出就變成這樣的流程了:
這樣,我們只需要Push程式碼足矣。
針對Linux或者macOS的構建,我們可以使用Travis-CI,針對Windows平臺的構建,我們可以使用AppVeyor。一個好訊息是,它們對於在GitHub上的開源專案都是可以免費構建的,並且和GitHub的倉庫結合地特別好,配置也比較簡單,可以說的非常良心了。
在使用它們之前,我們需要給予它們一定的許可權讓它們能夠訪問我們的GitHub倉庫。所以需要:
- 用你的GitHub賬號註冊它們,才能獲取你的倉庫列表。
- 在GitHub上生成token,賦予CI系統讀寫你的倉庫的許可權。生成token的具體操作可以檢視之前我寫的一篇針對hexo持久化構建的文章。
- 針對不同的CI平臺建立不同的配置檔案,好讓它們知道你要它們執行什麼操作。不過electron-vue很友好地為我們準備了Travis-CI的配置檔案模板
.travis.yml
和AppVeyor的配置檔案模板appveyor.yml
。所以我們基本上只需要在它們的基礎上小修改即可。
Travis-CI
註冊並登入Travis-CI後,找到你要構建的倉庫,然後開啟,點選設定進入如下頁面:
配置一下環境變數,名為GH_TOKEN
,token的值就是上一步我們在GitHub生成的token。等會會有用。
PicGo經過修改後的.travis.yml
如下:
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
# - os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
before_install:
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
install:
- nvm install 8.9
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
script:
- npm run release
- yarn run build:docs
branches:
only:
- master
after_script:
- cd docs/dist
- git init
- git config user.name "Molunerfinn"
- git config user.email "marksz@teamsz.xyz"
- git add .
- git commit -m "Travis build docs"
- git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
複製程式碼
拋去很多前置依賴(比如C++編譯庫之類的)和構建環境(是什麼系統,是什麼語言),那些都是electron-vue給我們預置好的。我們需要注意的僅僅是幾個部分:
- script
- branches
- after_script
script
是當系統和環境和依賴都準備好之後,你要CI執行的命令。在這裡我執行了兩個命令,一個是npm run release
,這個就是打包構建應用啦,並且執行了這個命令之後,electron-builder
會自動將生成好的安裝包推送到我們GitHub倉庫的draft release裡。另一個是構建PicGo主頁的命令yarn run build:docs
。
branches
宣告瞭你要在哪些分支在GitHub接收到了程式碼更新之後就構建,這裡我們自然選擇的是master。
after_script
是當你執行完script裡的指令碼之後要做的事。可以為空。對於我而言主要在這個部分將上一步構建好的PicGo主頁推送到GitHub的gh-pages
分支。當然如果你的應用有使用說明、文件之類的網站,也可以在這裡進行構建和推送。
注意到,在after_script
命令的最後一行,有個${GH_TOKEN}
,這個就是我們之前在Travis-CI配置裡配置的環境變數GH_TOKEN
。用環境變數的好處是不會暴露你的TOKEN,只有構建系統知道。
AppVeyor
有了之前的經驗,AppVeyor就更簡單了。註冊登入後,我們在主頁新增一個PROJECT,選中你要構建的倉庫。然後找到SETTING設定:
然後在左側的Genral
一欄的內容區中,找到構建的分支為master,以及設定我們僅在tag
更新的時候構建:
當然這個都是根據專案實際來的配置,我只是說PicGo的專案是這樣配置的。
然後在左側的Environment
區,找到環境變數配置,我們依然寫入GH_TOKEN
:
修改完配置都別忘了拉到底部去儲存!
這樣就算配置完了網頁端的。而現在我們來看看appveyor.yml
這個配置檔案:
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- npm install
- node --version
build_script:
#- yarn test
- npm run release
test: off
複製程式碼
依然是隻需要關注我們所關心的配置即可。一個是branches
,一個是build_script
。有了Travis-CI
的.travis.yml
的經驗,我相信你也能很快理解它。
經過上述配置之後,你已經實現了一個簡單的前端工程的自動化構建推送流程了。而今你只需要關注程式碼提交,應用的構建都將會由CI系統自動幫你完成。當然CI系統也不僅僅是拿來構建electron應用的,正如你所見的,你能想到的其他專案的構建、測試其實它都能幫你通過預定義好的指令碼完成。
釋出Release
當CI構建玩應用,會將其推送到你的GitHub的release頁面成為一個draft
(草稿),你可以編輯這個草稿,加上標題和更新說明,就可以點選publish
釋出你的新版本的應用啦。
electron應用的更新
electron應用的自動更新其實社群有很好的解決方案electron-updater。而electron-vue也在主程式的main/index.js
裡預先幫我們寫好了一段註釋的程式碼:
// import { autoUpdater } from 'electron-updater'
// autoUpdater.on('update-downloaded', () => {
// autoUpdater.quitAndInstall()
// })
// app.on('ready', () => {
// if (process.env.NODE_ENV === 'production') {
// autoUpdater.checkForUpdates()
// }
// }
複製程式碼
只要引入autoUpdater就能自動幫我們檢查更新和自動下載安裝更新。不過,凡事都有不過。這個方式雖然很簡單,但是它需要的條件比較嚴格,需要你擁有證照用於應用簽名。而macOS平臺下的證照需要你申請開發者,一年99$的費用讓我望而卻步。
於是我只能退而求其次,能不能通過查詢GitHub的release版本號,來比對當前版本,是否需要更新,並提醒使用者呢?經過嘗試,發現可行。我的實現方法如下:
我首先寫了一個updateChecker
的助手:
import { dialog, shell } from 'electron'
import db from '../../datastore'
import axios from 'axios'
import pkg from '../../../package.json'
const version = pkg.version
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
const checkVersion = async () => {
let showTip = db.read().get('picBed.showUpdateTip').value()
if (showTip === undefined) {
db.read().set('picBed.showUpdateTip', true).write()
showTip = true
}
// 自動更新的彈窗如果使用者沒有設定不再提醒,就可以去查詢是否需要更新
if (showTip) {
const res = await axios.get(release)
if (res.status === 200) {
const latest = res.data.name // 獲取版本號
const result = compareVersion2Update(version, latest) // 比對版本號,如果本地版本低於遠端則更新
if (result) {
dialog.showMessageBox({
type: 'info',
title: '發現新版本',
buttons: ['Yes', 'No'],
message: '發現新版本,更新了很多功能,是否去下載最新的版本?',
checkboxLabel: '以後不再提醒',
checkboxChecked: false
}, (res, checkboxChecked) => {
if (res === 0) { // if selected yes
shell.openExternal(downloadUrl)
}
db.read().set('picBed.showUpdateTip', !checkboxChecked).write()
})
}
} else {
return false
}
} else {
return false
}
}
// if true -> update else return false
const compareVersion2Update = (current, latest) => {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
let flag = false
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
flag = true
}
}
return flag
}
export default checkVersion
複製程式碼
然後在main/index.js
裡,我在app準備啟動的時候,呼叫這個更新助手:
// ...
import uploader from './utils/uploader.js'
app.on('ready', () => {
// ...
updateChecker()
// ...
})
複製程式碼
這樣就能在啟動應用的時候彈出更新提示:
總結
本文簡要地講述了electron應用用上CI系統幫我們自動化構建和推送,以及在沒有申請開發者,沒有證照用於應用的程式碼簽名的情況下如何告知使用者進行應用更新。要做一個健壯的應用就應該考慮到應用的版本釋出、版本更新和對使用者的更新通知。
本文很多都是我在開發PicGo
的時候碰到的問題、踩的坑。也許文中簡單的幾句話背後就是我無數次的查閱和除錯。希望這篇文章能夠給你的electron-vue
開發帶來一些啟發。文中相關的程式碼,你都可以在PicGo的專案倉庫裡找到,歡迎star~如果本文能夠給你帶來幫助,那麼將是我最開心的地方。如果喜歡,歡迎關注我的部落格以及本系列文章的後續進展。
注:文中的圖片除未特地說明之外均屬於我個人作品,需要轉載請私信