做者:凹凸曼-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 Fiddle,或者直接使用社區中提供的 腳手架。webpack
最初接觸 Electron,通常是被「使用前端技術棧生成多平臺桌面應用」的特性吸引。但在後續的開發中,纔會留意到 Electron 相比 NW.js 更爲複雜的進程模型:git
Electron 的架構能夠用下圖來表示:github
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 消息通知主窗口展現,達到秒開的效果。這個過程能夠用下圖表示:
除了窗口加載,在 Electron 中,require Node 模塊也是至關昂貴的操做。若是在渲染進程中直接使用大量的原生模塊,會嚴重拖慢頁面的打開時間,形成窗口可交互時間的延後,這對於桌面應用來講是災難性的體驗。Electron@5 以後的版本已經默認關閉了 BrowserWindow 的 nodeIntegration
功能,能夠看出 Electron 團隊也並不建議在渲染進程中直接使用原生模塊。
在桌面應用中,等待是很是難以忍受的,性能上的些許欠缺都會讓用戶以爲這是個套殼的網頁。如需使用原生模塊,咱們更建議使用異步的方式加載模塊,或是使用異步 IPC 在主進程中調用。另外,爲了優化用戶的體驗,咱們還須要在小動畫等方面下功夫,例如骨架屏等等。
Atom 團隊經過使用 V8 snapshot 能力,在生產環境中去掉了低性能的 require 調用,將 Electron 應用的加載性能提高了 30%,同時還提高了應用的安全性能,這篇文章 How Atom Uses Chromium Snapshots 對他們的作法作了詳細介紹。
啓用骨架屏先後對比:
性能優化的方式並不侷限於上面的方式。例如開啓 electron-builder 的 asar 功能,在打包時將源碼生成二進制的 asar 文件,下降 require 操做的代價的同時,也能稍許減小空間佔用,代價是沒法對 asar 內的文件使用 child_process.spawn
;須要密集計算的功能,能夠開多一個渲染進程來跑,或是使用 require('child_process').spawn
開子進程來跑,避免阻塞主進程,形成應用卡死。
一樣受到開發者詬病的,還有 Electron 應用的體積 。一個空 Electron 項目,在打包後就會佔據近上百兆空間。Electron 的應用體積之因此大,除了自帶的 Chromium 內核,還有大部分體積是來自用戶安裝的 node_modules。
使用 electron-builder 打包 Electron 應用時,若是不加處理,會將 node_modules 內的內容全數打包,致使應用體積偏大。針對這種狀況,咱們能夠進行一系列優化:
使用 yarn autoclean
命令進行清理。node_modules 目錄中,包含着大量的 README 文件、文檔等內容,這部分文件在生產環境中並不是必要。若是項目中使用 yarn
進行依賴管理,則可使用 yarn autoclean
命令。這個命令會初始化一份默認的配置文件 .yarnclean
。yarn
在安裝依賴後,將會自動根據 .yarnclean
進行依賴清理。
# 默認的 .yarnclean 文件大體以下:
# test directories
__tests__
test
tests
powered-test
# asset directories
docs
doc
website
assets
# examples
example
examples
...
複製代碼
使用雙 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 中排除,避免重複打包。
目前,項目的大部分能力依然是基於 Electron 提供的能力實現的。這至關於與 Electron 嚴重耦合,不利於項目中個別能力的複用。將來,咱們但願對項目的架構進行調整,對核心能力進行插件化改造,方便能力的移植與複用,甚至將來的研發上雲,這有賴於項目核心能力的 Web 化。固然,Web 化也會帶來額外的性能損耗,這會對咱們項目的性能提出新的要求。
項目的穩定性也是將來須要努力的方向。咱們有時會收到用戶關於應用閃退、卡死等現象的反饋,卻苦於沒法復現,不少時候難以解決用戶反饋的問題。將來,咱們須要在項目中加入異常監控上報的機制,收集操做系統信息、內存使用量等關鍵信息,在 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),不定時推送文章: