Electron開發初體驗

Heng溫發表於2018-03-12

需求背景

平時總會寫markdown,markdown整體語法用起來很方便,但依然有晦澀的地方,比如表格。markdown的表格語法寫起來很容易出錯,而且每行每列單元格里的內容長短不一編輯器裡就很容易亂掉,所以我在寫表格時候都是藉助Tables Generator來寫的,但是這個網站不能儲存多個模板,每次寫不同的表格都要把列數,表頭資訊來回改,很麻煩,於是打算自己照著Table Generator寫一個簡單的,能儲存表頭資訊的東西出來。先看一哈大致的樣子:

Electron開發初體驗

動手前的思考

最初在考慮的是要不要寫一個差不多的簡單的頁面,但是我個人不太喜歡總開新的tab來回切換,所以突然想到可以做成一個簡單的桌面應用,想用的時候可以直接從Dock啟動,而且Electron有了解過但沒實際用過,也可以嚐嚐鮮,就決定用Electron直接做成一個小應用了。

工程搭建

在這個"不用腳手架不舒服" + "不用框架不舒服"的時代,搭建工程當然是選擇一款靠譜的腳手架了,開發環境 + 打包構建都能通過命令列搞定,極大程度地節省了時間,感謝開源貢獻者吧~這裡我選擇了electron-vue這個模板,基於vue-cli的,初始化專案很簡單,直接執行:

vue init simulatedgreg/electron-vue my-project
複製程式碼

然後根據提示輸入完專案名,專案描述,依賴和構建工具(electron-builder或者electron-packager)後,一個專案就搭建完成了,進入目錄執行:

yarn && yarn dev
複製程式碼

然後專案就以開發模式執行起來了

Electron開發初體驗

後續的開發工作,如果你的應用對於系統級別的API需求不大,事實上和開發網頁的體驗並沒有什麼區別。比如我要完成的這個小工具就和開發網頁的體驗差不多。

核心概念

實現的思路比較簡單:為表格的每一個單元格設定contenteditable,這樣整個表格的內容都是可以隨意編輯的,然後再通過MutationObserver監聽表格內容的變化,構造出正確的資料結構即可。

為th和td新增 contenteditable 使其內容可以編輯:

<table id="table">
    <tbody>
      <tr>
        <th class="col-mark"></th>
        <th data-row="0" :data-col="index" v-title="item.text"
          v-for="(item, index) in columns" :key="item.key" contenteditable
          :class="{'active': index === acCol}"
        ></th>
      </tr>
    </tbody>
</table>
複製程式碼

然後在渲染完成後使用MutationObserver監聽變化:

mounted () {
    this.targetNode = document.getElementById('table')
    // this.handleMutation是具體處理回撥
    this.observer = new MutationObserver(this.handleMutation)
    this.observer.observe(this.targetNode, this.config)
}
複製程式碼

這裡的MutationObserver的配置是{ subtree: true, characterData: true, childList: true }作用分別是:

  • characterData: 目標的資料被修改時觸發,比如直接編輯td,th裡的內容時,就是對其textContent的修改。
  • subtree: 使MutationObserver也能響應後代元素內容的變化,因為我們只在table上繫結一個MutationObserver,所以使用這個屬性。
  • childList: 目標(包括後代節點)的子元素(包括文字元素)新增或者被刪除時觸發。比如單元格中的內容從無到有和從有到無的兩種邊界情況。

核心概念主要就用到了這兩個概念,樣式上選擇了papercss,簡單的功能搭配簡潔的風格。

複製到剪貼簿

表格資訊寫好之後,最後的功能就是生成對應的markdown內容然後複製到貼上板了。Electron提供了clipboard API,直接呼叫clipboard.writeText就能把內容寫入貼上板:

import { clipboard } from 'electron'

let text = 'xxx'
clipboard.writeText(text)
複製程式碼

這裡複製成功後可以給出一個提示資訊,我們在Electron開發的內容一般是在render程式的,在render程式中可以直接使用HTML5 Notification API來實現提示:

let myNotification = new Notification('Table Generator', {
    body: 'Copy successfully~'
})
setTimeout(() => {
    myNotification.close()
}, 2000)
複製程式碼

實際效果為一個兩秒後自動消失的提示框:

Electron開發初體驗

主程式與渲染程式的通訊

這裡要實現的效果是通過自定義的快捷鍵刪除左側模板列表中的模板,但是快捷鍵註冊只能在主程式通過globalShortcut註冊,而對於刪除行為的響應(二次確認的彈窗)是在渲染程式,所以設計到了主程式和渲染程式的通訊。

渲染程式是主程式中建立的一個BrowserWindow例項,例項的webContents屬性是對渲染程式的引用,所以主程式可以直接通過webContents傳送事件:

// 建立的渲染程式
mainWindow = new BrowserWindow({
    height: 560,
    minHeight: 450,
    width: 1000,
    minWidth: 760,
    titleBarStyle: 'hiddenInset',
    show: false,
    backgroundColor: '#fff'
})
// 註冊快捷鍵
globalShortcut.register('Cmd+D', () => {
    // 直接通過mainWindow.webContents傳送事件
    mainWindow.webContents.send('del-tpl')
})
複製程式碼

而在對應的render程式,可以通過ipcRenderer監聽訊息:

ipcRenderer.on('del-tpl', () => {
  // 觸發modal彈出
  this.$refs['del-btn'].click()
})
複製程式碼

一些簡單的優化

事實上通過一些簡單的配置,就可以讓你的應用體驗更好:

  • 建立無邊框視窗:無邊框視窗會讓應用整體變得更美觀,在建立BrowserWindow時通過titleBarStyle: 'hiddenInset'實現(針對Mac系統)
  • 在建立BrowserWindow時通過show: false隱藏視窗,在'ready-to-show'事件觸發時手動呼叫視窗例項的show()方法,保證視窗渲染完再展示。
  • 通過將視窗背景顏色設定成渲染程式背景一樣的顏色,讓應用顯得更快。

總結

本次初步的嘗試並沒有用到太多系統級別的API,基本和開發Web頁面體驗一樣,文中提到的API和優化點都是文件上可以找到的,本次實踐只是對Electron的一次涉獵,後續可以考慮將各種操作和提示都遷移到原生的API,或者再加入其它功能,不過用來生成markdown內容“初心”已經達到了~

原始碼在GayHub上,有興趣的同學也可以自己安裝依賴構建體驗一哈,順便點個star~

相關文章