Electron-vue開發實戰3——跨平臺的一些相容措施

Molunerfinn發表於2018-03-20

前言

前段時間,我用electron-vue開發了一款跨平臺(目前支援Mac和Windows)的免費開源的圖床上傳應用——PicGo,在開發過程中踩了不少的坑,不僅來自應用的業務邏輯本身,也來自electron本身。在開發這個應用過程中,我學了不少的東西。因為我也是從0開始學習electron,所以很多經歷應該也能給初學、想學electron開發的同學們一些啟發和指示。故而寫一份Electron的開發實戰經歷,用最貼近實際工程專案開發的角度來闡述。希望能幫助到大家。

預計將會從幾篇系列文章或方面來展開:

  1. electron-vue入門
  2. Main程式和Renderer程式的簡單開發
  3. 引入基於Lodash的JSON database——lowdb
  4. 跨平臺的一些相容措施
  5. 通過CI釋出以及更新的方式
  6. ...(想到再寫)

說明

PicGo是採用electron-vue開發的,所以如果你會vue,那麼跟著一起來學習將會比較快。如果你的技術棧是其他的諸如reactangular,那麼純按照本教程雖然在render端(可以理解為頁面)的構建可能學習到的東西不多,不過在main端(electron的主程式)應該還是能學習到相應的知識的。

如果之前的文章沒閱讀的朋友可以先從之前的文章跟著看。

跨平臺的重要性

雖然electron在大多數情況下的跨平臺措施已經幫我們做得很好了。不過需要注意的是,不同平臺必然存在細節上的差異。我們在書寫跨平臺應用的時候,如果只在自己書寫平臺下測試通過的話是不足以說明我們的應用是健壯的。(當然如果你只想提供給某個平臺那另當別論)所以針對不同的釋出平臺,就需要做一些相容性措施。

就我自己的感受而言,macOS平臺支援的特性相對比較多,而這裡面又很多是獨有的,所以很多能在macOS上實現的功能卻不一定能在windows上實現。所以對於windows使用者而言,在保證整體應用的可用性的情況下,就有可能要相應地做一些妥協和犧牲。不過在windows上的一些操作習慣也可以反過來服務於macOS平臺。這點我會在下面給出一個例子詳細說明。

留意不同平臺的獨有功能

在開發electron應用的時候,很多時候我們只注意去查詢api名,卻容易忽視這個api能夠使用的平臺。在官方文件裡,對於一些獨佔的api,大多都會有標識標出:

Electron-vue開發實戰3——跨平臺的一些相容措施

不過需要注意的是一些未有平臺標識的api裡的配置項,也有可能是某個平臺的獨佔:

Electron-vue開發實戰3——跨平臺的一些相容措施

平時開發的過程中,用到文件的地方還是需要細細留心,避免後續不必要的麻煩。

跨平臺措施入門

上面講了這麼多,該到例項的時候了。在electron應用中,通常來說renderer程式的東西不需要做太多的跨平臺措施——畢竟不管是哪個平臺,都是跑在Chrome裡的頁面。所以大多數情況下,這個方面的工作會放在main程式裡。不過也有例外:

title-bar的操作區處理

下面是PicGo的windows版:

Electron-vue開發實戰3——跨平臺的一些相容措施

下面是PicGo的macOS版:

Electron-vue開發實戰3——跨平臺的一些相容措施

可以發現除了顏色有些區別之外,頂部的title-bar操作欄也有些區別。macOS的程式視窗習慣將視窗的縮放、關閉按鈕放在視窗的左上角。而windows程式則相反,它們喜歡放在視窗的右上角。所以為了迎合使用者的操作習慣,我們在開發electron程式的時候也應該注意到這一點。

當然,如果是通過普通的BrowserWindow建立的視窗,那麼將會自動擁有常見的macOS、windows的頂部欄,以及預設的樣式。

我在這裡想說的是如果想要更加美觀的介面,通常我們喜歡「沉浸式」的頂部欄。對於macOS而言,沉浸式的頂部欄就是將頂部欄的三個操作按鈕直接「嵌入」視窗主題的左上角。而對於windows而言,只能刪去頂部的三個操作按鈕,自己用前端的方式來實現了。所以這個地方兩個平臺的差異性就出來了。

main程式裡建立該視窗的時候,主要程式碼如下:

const createSettingWindow = () => {
  const options = {
    height: 450,
    width: 800,
    show: false, // 當window建立的時候不用開啟
    center: true,
    fullscreenable: false,
    resizable: false,
    title: 'PicGo',
    vibrancy: 'ultra-dark', // 視窗模糊的樣式
    transparent: true,
    titleBarStyle: 'hidden', // title-bar的樣式——隱藏頂部欄的橫條,把操作按鈕嵌入視窗
    webPreferences: {
      backgroundThrottling: false
    }
  }
  if (process.platform === 'win32') { // 如果平臺是win32,也即windows
    options.show = true // 當window建立的時候開啟
    options.frame = false // 建立一個frameless視窗,詳情:https://electronjs.org/docs/api/frameless-window
    options.backgroundColor = '#3f3c37'
  }
  settingWindow = new BrowserWindow(options)

  settingWindow.loadURL(settingWinURL)

  settingWindow.on('closed', () => {
    settingWindow = null
  })
}
複製程式碼

主要的工具是通過process.platform來判斷不同的平臺。當前可能的值有:

  • 'aix'
  • 'darwin'
  • 'freebsd'
  • 'linux'
  • 'openbsd'
  • 'sunos'
  • 'win32'

在這裡我們基本上只需要關心darwin(macOS)、win32(windows)、linux(Linux)這三個平臺即可。注意,由於electron的對於renderer程式的加持,在renderer程式裡也能直接使用process.platform來判斷當前的作業系統。這是一個很方便的特性。

針對windows平臺,由於採用了frameless-window,所以我們需要手動「繪製」頂部的縮放和關閉按鈕,並配上相應的事件來模擬真實的按鈕。

<div class="fake-title-bar">
  PicGo - {{ version }}
  <div class="handle-bar" v-if="process.platform === 'win32'"><!-- 如果是windows平臺 -->
    <i class="el-icon-minus" @click="minimizeWindow"></i>
    <i class="el-icon-close" @click="closeWindow"></i>
  </div>
</div>
複製程式碼

相應的事件如下:

minimizeWindow () {
  const window = BrowserWindow.getFocusedWindow()
  window.minimize()
},
closeWindow () {
  const window = BrowserWindow.getFocusedWindow()
  window.close()
},
複製程式碼

簡單來說就是呼叫了BrowserWindow的方法來獲取當前啟用的視窗,然後再對這個視窗進行縮小或關閉的操作。其實也不難對吧!

工作列圖示互動

針對不同的平臺,我對PicGo的工作列圖示互動也有所區別。對於macOS而言,點選頂部選單欄的時候會彈出一個小視窗:

Electron-vue開發實戰3——跨平臺的一些相容措施

由於macOS的頂部欄圖示可以接受拖拽事件,所以就針對macOS的頂部欄製作了頂部欄圖示對應的小視窗。讓大部分操作不經過主視窗也能實現。而對於windows而言,沒有頂部欄,取而代之的是位於底部欄的右側的工作列,通常點選工作列裡的圖示就會把應用的主視窗調出來。所以為了迎合不同平臺的操作習慣,我對於這個地方也做了相應的相容性適配:

tray.on('click', () => { // 不管是頂部欄的圖示還是工作列的圖示都是Tray元件生成的
  if (process.platform === 'darwin') { // 如果是macOS平臺
    let img = clipboard.readImage()
    let obj = []
    if (!img.isEmpty()) {
      // 從剪貼簿來的圖片預設轉為png
      const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
      obj.push({
        width: img.getSize().width,
        height: img.getSize().height,
        imgUrl
      })
    }
    toggleWindow() // 開啟小視窗
    setTimeout(() => {
      window.webContents.send('clipboardFiles', obj)
    }, 0)
  } else {
    window.hide()
    if (settingWindow === null) { // 如果主視窗未建立
      createSettingWindow() // 建立
      settingWindow.show() // 並開啟
    } else {
      settingWindow.show() // 如果已存在,開啟
      settingWindow.focus() // 並啟用
    }
  }
})
複製程式碼

視窗關閉與應用退出

在windows平臺上,通常我們把應用的視窗都關了之後也就預設把這個應用給退出了。而如果在macOS系統上卻不是這樣。我們把應用的視窗關閉了,但是並非完全退出這個應用。所以為了實現這個操作習慣,我們也可以增加一個情況判斷:

app.on('window-all-closed', () => { // 當視窗都被關閉了
  if (process.platform !== 'darwin') { // 如果不是macOS
    app.quit() // 應用退出
  }
})
複製程式碼

總結

本文簡要地講述了electron應用在跨平臺開發的時候的一些注意事項。可能很多人會覺得奇怪我為啥把這個章節單獨拎出來講。很多時候我們只關注於應用的開發過程,把應用的功能實現是很多情況下的「終極」目標。然而真實情況是,應用的功能實現只是「基本」目標。一個應用要給使用者使用的話必然不僅要考慮到應用的功能,還必須考慮使用者的使用習慣。要站在使用者的角度來做應用。而不是做自嗨型的應用。所以這篇文章也希望能夠幫助想要開發electron應用的你。

本文很多都是我在開發PicGo的時候碰到的問題、踩的坑。也許文中簡單的幾句話背後就是我無數次的查閱和除錯。希望這篇文章能夠給你的electron-vue開發帶來一些啟發。文中相關的程式碼,你都可以在PicGo的專案倉庫裡找到,歡迎star~如果本文能夠給你帶來幫助,那麼將是我最開心的地方。如果喜歡,歡迎關注我的部落格以及本系列文章的後續進展。

注:文中的圖片除未特地說明之外均屬於我個人作品,需要轉載請私信

相關文章