Electron 在 Taro IDE 的開發實踐

做者:凹凸曼-littly前端

背景

Taro IDE 是一款咱們正在精心打造的一站式移動端研發工做臺。除了須要實現 Taro 從建立項目到預覽、編譯的所有能力,還須要打通用戶測試、調試、監控等一系列流程。爲了提高開發體驗,僅僅一個命令行工具是遠遠不夠的,咱們須要開發一款桌面客戶端,並同時提供 Windows、MacOS 等不一樣系統的版本。node

Electron 最初是 Github 爲 Atom 編輯器開發的桌面應用框架。Electron 將 Chromium 與 Node 合併到同個運行時環境中,賦予了 Web 代碼與底層操做系統進行交互的能力,並在打包時生成 Windows、MacOS、Linux 等平臺的桌面應用。比起原生的桌面應用開發框架,Electron 在性能、應用體積方面會稍遜一籌,但 Electron 支持打包多個平臺的桌面應用,在業界已經有 VSCode、Atom、Slack 等綜合體驗拔羣的成功案例,咱們認爲 Electron 徹底知足咱們的需求。react

介紹 Electron

若是隻想體驗一下 Electron,最快的方式是使用 Electron Fiddle,或者直接使用社區中提供的 腳手架webpack

最初接觸 Electron,通常是被「使用前端技術棧生成多平臺桌面應用」的特性吸引。但在後續的開發中,纔會留意到 Electron 相比 NW.js 更爲複雜的進程模型:git

Electron 的架構能夠用下圖來表示:github

img

Electron 項目中,運行 package.json 的 main 腳本的進程被稱爲主進程。主進程經過建立 web 頁面來展現用戶界面。這些用戶界面都運行在彼此隔離的渲染進程中。web

Electron 主進程支持 Node API,而且可直接與操做系統進行底層交互,彈出系統通知、文件系統讀寫、調用硬件設備等。typescript

Electron 渲染進程默認只能與自身的 Web 內容進行交互。在打開 nodeIntegration 功能後,渲染進程也能夠具有操做 Node 的能力。渲染進程也沒法直接操做彈窗(Dialog)、系統通知(Notification)等,這些功能都須要經過 Electron 提供的 IPC/remote 機制在主進程中調用。json

而且在後續 Electron 的升級中,這些約束也可能由於安全、性能的緣由進行調整。能夠說,Electron 的開發體驗並不太美好,但正是這種開發體驗與用戶體驗之間的博弈,保證了 Electron 應用在性能、安全方面的表現。瀏覽器

開發工做流

咱們使用社區提供的 electron-react-typescript 做爲項目的初始腳手架。閱讀 package.json 文件,咱們能夠了解到,這個項目使用 webpack 進行主進程和渲染進程的打包,src/main/main.ts 文件就是主進程的入口。

Electron 的 BrowserWindow 類負責建立和控制瀏覽器窗口,app 對象則能夠控制應用程序的各個事件與生命週期。 主進程的代碼大體以下:

import { app, BrowserWindow } from 'electron'

let win
app.on('ready', () => {
  win = new BrowserWindow({ width: 800, height: 600 });
  win.loadURL(`http://localhost:2003`);
  // xxx
});

app.on('activate', () => {})
app.on('window-all-closed', () => {})
複製代碼

渲染進程 src/renderer/app.tsx 就一個普通的頁面,這裏再也不贅述。安裝依賴後,使用 yarn start-dev ,便可啓動項目的預覽服務。

這個項目使用 webpack 來打包項目代碼,這樣處理有兩個好處。一是經過 webpack 處理,咱們能夠減小運行時的 require 調用,對 Electron 應用加載性能有必定幫助;二是藉助 webpack 的 tree shaking 能力,未使用的代碼也會被輕鬆移除,能夠有效減小安裝包體積。

爲了打包 electron 項目,咱們須要至少兩份 webpack 配置文件,一份打包主進程文件,指定 target 爲 electron-main,另外一份打包渲染進程,target 設置爲 electron-renderer

爲了輔助 Electron 項目的調試工做,咱們能夠安裝 Devtron。Devtron 是 Electron 提供的開發調試插件。在開發者工具中加入 Devtron 後,項目中的 IPC 通訊、查看項目依賴、事件等信息,均可以在開發者工具中直接查看。

若有須要,咱們還能夠安裝其餘的開發者工具擴展,例如 Redux、React 等,只須要在主進程中運行:

// main.js

const {
  default: installExtension,
  REACT_DEVELOPER_TOOLS,
  REACT_PERF,
  REDUX_DEVTOOLS
} = require('electron-devtools-installer')
const extensions = [REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS, REACT_PERF]

extensions.forEach(extension => {
  try {
    installExtension(extension)
  } catch (e) {
    console.error(e)
  }
})
複製代碼

至此,咱們的開發環境搭建完畢,能夠開始進行業務代碼的開發了。

優化

業務代碼開發完畢後,就到了優化的環節了。這裏主要從 Electron 應用的性能與體積兩方面來說。

性能

Electron 在性能方面一直受到廣大開發者的詬病。窗口打開慢,加載時間長都是老生暢談的話題。這些問題該如何解決呢?

答案是預加載。在展現登陸窗口時,咱們能夠提早將主窗口開啓並設置隱藏,預加載主窗口的靜態資源。用戶登陸後,再經過 IPC 消息通知主窗口展現,達到秒開的效果。這個過程能夠用下圖表示:

img

除了窗口加載,在 Electron 中,require Node 模塊也是至關昂貴的操做。若是在渲染進程中直接使用大量的原生模塊,會嚴重拖慢頁面的打開時間,形成窗口可交互時間的延後,這對於桌面應用來講是災難性的體驗。Electron@5 以後的版本已經默認關閉了 BrowserWindow 的 nodeIntegration 功能,能夠看出 Electron 團隊也並不建議在渲染進程中直接使用原生模塊。

在桌面應用中,等待是很是難以忍受的,性能上的些許欠缺都會讓用戶以爲這是個套殼的網頁。如需使用原生模塊,咱們更建議使用異步的方式加載模塊,或是使用異步 IPC 在主進程中調用。另外,爲了優化用戶的體驗,咱們還須要在小動畫等方面下功夫,例如骨架屏等等。

Atom 團隊經過使用 V8 snapshot 能力,在生產環境中去掉了低性能的 require 調用,將 Electron 應用的加載性能提高了 30%,同時還提高了應用的安全性能,這篇文章 How Atom Uses Chromium Snapshots 對他們的作法作了詳細介紹。

啓用骨架屏先後對比:

img

性能優化的方式並不侷限於上面的方式。例如開啓 electron-builder 的 asar 功能,在打包時將源碼生成二進制的 asar 文件,下降 require 操做的代價的同時,也能稍許減小空間佔用,代價是沒法對 asar 內的文件使用 child_process.spawn ;須要密集計算的功能,能夠開多一個渲染進程來跑,或是使用 require('child_process').spawn 開子進程來跑,避免阻塞主進程,形成應用卡死。

體積

一樣受到開發者詬病的,還有 Electron 應用的體積 。一個空 Electron 項目,在打包後就會佔據近上百兆空間。Electron 的應用體積之因此大,除了自帶的 Chromium 內核,還有大部分體積是來自用戶安裝的 node_modules。

使用 electron-builder 打包 Electron 應用時,若是不加處理,會將 node_modules 內的內容全數打包,致使應用體積偏大。針對這種狀況,咱們能夠進行一系列優化:

  1. 使用 yarn autoclean 命令進行清理。node_modules 目錄中,包含着大量的 README 文件、文檔等內容,這部分文件在生產環境中並不是必要。若是項目中使用 yarn 進行依賴管理,則可使用 yarn autoclean 命令。這個命令會初始化一份默認的配置文件 .yarncleanyarn 在安裝依賴後,將會自動根據 .yarnclean 進行依賴清理。

    # 默認的 .yarnclean 文件大體以下:
     
     # test directories
     __tests__
     test
     tests
     powered-test
     
     # asset directories
     docs
     doc
     website
     assets
     
     # examples
     example
     examples
     
     ...
    複製代碼
  2. 使用雙 package.json 架構。node_modules 目錄中,除了生產環境須要用到的依賴,還存在着不少 devDependencies,這部分依賴是不該該被打包的。爲了解決這個問題,electron-builder 提供了雙 package.json 架構。具體來講,electron-builder 推薦用戶將 Electron 應用依賴劃分爲兩部分:開發依賴以及生產依賴。用戶使用項目根目錄的 package.json 來管理開發依賴,而使用項目的應用文件夾下的 package.json 管理生產依賴。electron-builder 僅會打包應用文件夾下的依賴。

    在這個改動後,安裝依賴時還須要經過 electron-builder install-app-deps 命令安裝應用依賴。這個操做推薦放在 package.json 內的 post-install 腳本中。

    electron-builder@8 後,並不會打包 devDependencies 內的依賴。這意味着咱們能夠經過這個途徑來避免開發依賴被打包的問題。若是項目使用了 webpack 之類的工具進行打包,則須要注意將 webpack 已經打包過的資源從 dependencies 中排除,避免重複打包。

將來

能力 Web 化

目前,項目的大部分能力依然是基於 Electron 提供的能力實現的。這至關於與 Electron 嚴重耦合,不利於項目中個別能力的複用。將來,咱們但願對項目的架構進行調整,對核心能力進行插件化改造,方便能力的移植與複用,甚至將來的研發上雲,這有賴於項目核心能力的 Web 化。固然,Web 化也會帶來額外的性能損耗,這會對咱們項目的性能提出新的要求。

img

崩潰處理

項目的穩定性也是將來須要努力的方向。咱們有時會收到用戶關於應用閃退、卡死等現象的反饋,卻苦於沒法復現,不少時候難以解決用戶反饋的問題。將來,咱們須要在項目中加入異常監控上報的機制,收集操做系統信息、內存使用量等關鍵信息,在 Crash 時進行上報,甚至推送告警消息。這有利於開發人員進一步瞭解用戶的使用過程,方便問題的復現。

小結

在開發桌面應用時,Electron 在效率上有很大的優點。幾行 JS 代碼就能夠啓動桌面客戶端,大大下降了開發門檻。但 Electron 在性能、體積等方面也存在着軟肋。若是在前期開發時沒有通過充分思考,頗有可能會在後期優化時付出慘痛的代價。在這個項目中,咱們的優化工做還遠遠不夠,後續有更多突破會分享給你們。


參考資料

[1]Electron: www.electronjs.org/

[2]Electron Fiddle: www.electronjs.org/fiddle

[3]腳手架: github.com/search?q=el…

[4]NW.js: nwjs.io/

[5]electron-react-typescript: github.com/Robinfr/ele…

[6]src/main/main.ts: github.com/Robinfr/ele…

[7]src/renderer/app.tsx: github.com/Robinfr/ele…

[8]Devtron: www.electronjs.org/devtron

[9]How Atom Uses Chromium Snapshots: flight-manual.atom.io/behind-atom…


歡迎關注凹凸實驗室博客:aotu.io

或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章:

image
相關文章
相關標籤/搜索