以前發過一個裝逼的朋友圈:感謝 Electron,我如今有兩個身份了:前端開發和 Mac 端開發。javascript
今年開始了一個全新的產品,但項目組並無 Mac 的開發人員,而後咱們前端就順利的扛下了這面大旗,而且選擇了火爆的 Electron(4w+ star)。html
年末了,看你們在寫各類各樣的年終總結,想着我和 Electron 也打情罵俏了好幾個月,也該寫點東西。不過 Electron 已經夠火了,能 google 到不少相似如何使用 Electron 開發桌面端應用的文章,好比用Electron開發桌面應用,也能找到官方中文文檔,雖然滯後了一點。因此我也就再也不贅述怎樣從頭寫一個 Electron 應用了,在閱讀下面的內容以前,最好能先跟隨官方的 Quick Start 走一遍。前端
Electron提供了一個Nodejs的運行時,專一於構建桌面應用,同時使用web頁面來做爲應用的GUI,你能夠將其看做是一個由JavaScript控制的迷你版的Chromium瀏覽器。html5
夠火自沒必要說,Electron 的做者和 NW.js(原 node-webkit)是同一人,你們能夠去知乎圍觀維護一個大型開源項目是怎樣的體驗?這個問題下做者的回答(評論區提出該知乎問題下還有一個回答對此存有異議:https://www.zhihu.com/questio...,你們自行分辨)。官方也有 Electron 和 NW.js 的對比,相信做者在新的項目中有了新的思考,因此選擇 Electron 是個對的選擇,而且咱們只須要開發 Mac 端(組內有 Windows 開發人員),沒有對 XP 系統兼容的要求。java
也有考慮過像 MacGap 這樣的解決方案,不過 MacGap 是基於 webkit 內核的,並不支持 IndexedDB,而咱們的項目依賴的第三方服務是依賴 IndexedDB 作爲本地存儲的。而且 Electron 更強大,好比新開 webview 加載第三方頁面,而且能夠預加載(preload) JavaScript來做爲 jssdk 供 web 應用使用。node
因此 Electron,就是你了!git
我的感受,理解 Electron 最重要應該就是理解主進程(Main Process)和渲染進程(Render Process)了。理解了這二者,其餘內容花費些時間查查API文檔便可。github
Electron 中,入口是一個 js 文件(和 NW.js 不一樣,入口是 html 文件),運行這個入口文件(一般會是 package.json 裏的 main 腳本)的進程稱做主進程,在主進程使用 BrowserWindow
模塊能夠建立並管理 web 頁面,也就是應用的 GUI。web
const {BrowserWindow} = require('electron') // 主進程建立web頁面 let someWindow = new BrowserWindow(winOpts) // 加載本地的文件 someWindow.loadURL('file://' + __dirname + '/index.html')
在主進程建立的一個個web頁面也都運行着本身的進程,即渲染進程,渲染進程各自獨立,各自管理本身的頁面,能夠想象是瀏覽器一個個的 tab。shell
咱們知道,Web 頁面由於安全限制,不能直接訪問原生的GUI資源(好比dialog、電源監控),Electron 中也是同樣,渲染進程若是想要進行原生的GUI操做,就必須和主進程通信,請求相應的GUI操做。
Electron 提供了幾種渲染進程和主進程通訊的方式:
一種是使用ipcMain和ipcRenderer模塊,在渲染進程中使用ipcRender模塊向主進程發送消息,主進程中ipcMain接收消息,進行操做,若是還須要反饋,則通知渲染進程,渲染進程根據接收的內容執行相應的操做:
// 渲染進程中 const {ipcRenderer} = require('electron') ipcRender.send('somemsg', data); ipcRender.on('replaymsg', (evt, otherData) => { console.log(otherData) }) // 主進程中 const {ipcMain} = require('electron') ipcMain.on('somemsg', (evt, data) => { console.log(data) evt.sender.send('replymsg', otherData); }); // 同時Electron 也提供了同步的方式
不過切忌用 ipc 傳遞大量的數據,會有很大的性能問題,嚴重會讓你整個應用卡住。
第二種是直接在渲染進程使用remote模塊,remote 模塊能夠直接獲取主進程中的模塊。這種方式實際上是第一種方式的簡化。
// 在渲染進程打開提示對話框 const {dialog} = require('electron').remote dialog.showMessageBox({ opts });
第三種是主進程向渲染進程發送消息
this.webviewWindow.webContents.send('ping');
第四種是渲染進程之間的通訊
若是數據不須要實時性,只是渲染進程之間數據的共享,那麼使用官方的建議便可:How to share data between web pages?。若是要求實時性,須要配合前幾種種方式實現。
// 主進程 // 兩個窗口互相獲取對方的窗口 id, 併發送給渲染進程 win1.webContents.send('distributeIds',{ win2Id : win2.id }); win2.webContents.send('distributeIds',{ win1Id : win1.id }); // 渲染進程 // 經過 id 獲得窗口 remote.BrowserWindow.fromId(win2Id).webContents.send('someMsg', 'someThing');
Electron 內集成了 Nodejs,大大的方便了開發。Nodejs 在主進程和渲染進程中均可以使用,上面說到,渲染進程由於安全限制,不能直接操做原生 GUI。雖然如此,由於集成了 Nodejs,渲染進程也有了操做系統底層 API 的能力,Nodejs 中經常使用的 Path、fs、Crypto 等模塊在 Electron 能夠直接使用,方便咱們處理連接、路徑、文件MD5等,同時npm還有成千上萬的模塊供咱們選擇。
尤爲對於 Electron 不方便實現的功能,Nodejs 可能有奇效。咱們應用中用戶須要下載文件消息的文件,須要支持同事下載多個,而且須要給出進度,Electron 並無提供一個好用的下載接口,因此咱們使用 Nodejs 的 http、fs 模塊結合 Electron 的 dialog 模塊實現了文件下載,而且實現了下載進度以及下載超時錯誤提示。
不考慮兼容性應該是前端碼農的夢想之一吧。Electron 使用 Chromium 來展現 web 頁面,也就是咱們開發只須要兼容 Chromium 瀏覽器便可,也就是說好多屬性能夠肆無忌憚的用:播放語音直接使用 HTML5 audio、大量數據存儲使用數據庫 IndexedDB、難搞的佈局直接使用 Flexbox、方便的檢測在線離線等等。
同時 Electron 對一些 HTML5 的特性進行了加強:
桌面通知,你能夠直接使用 html5 的 notification,Electron會將其轉化成爲系統原生的桌面通知;
File 對象,在Web應用中咱們能獲得的通常是相似 C:/fakePath/xxx.docx
的假路徑,Electron在 File 對象上增長了一個path屬性,能夠用來獲取選擇的文件在文件系統中的真實路徑。
a
標籤的 download 屬性,在 Web 應用中 a
標籤增長 download 屬性會強制瀏覽器下載,Electron 中會直接調起系統下載框下載,若是沒有特殊需求推薦這種方式。
渲染進程調試和在瀏覽器中的調試徹底一致。前面提到每一個渲染進程徹底獨立,當你建立了多個web頁面,每一個頁面均可以打開對應了調試工具,你能夠和瀏覽器調試同樣查看DOM、查看log、監聽網絡請求等等。
同時 Electron 集成了 Nodejs,因此你在控制檯或者斷點時也可以調試 Nodejs 的 API,甚至由於渲染進程可使用 remote 模塊直接使用主進程的模塊,你能夠直接獲取到這些數據以方便調試。
此次的新產品有一個需求,須要在客戶端內加載 Webview 應用,而且要提供 jssdk 供 Web 應用使用,以獲取更多的本地能力。其實 Electron 自然的優點能夠加載外部應用的。可是要考慮的問題仍是比較多的,好比要展現頁面加載的進度、監聽頁面什麼時候加載完成、頁面 DOM 什麼時候加載完成、服務端一些302重定向如何(好比一些跳轉認證)處理、如何給 Web 應用提供 jssdk 等等。
Electron 提供了一系列的事件來監聽頁面的加載,細化到了頁面開始加載、頁面加載完成、頁面加載失敗、DOM Ready、框架加載 (did-frame-finish-load)、重定向(did-get-redirect-request)等等,經過監聽這些事件能夠對頁面狀態進行處理。
另外,如何給 Web 應用提供 jssdk 呢?咱們須要依賴 BrowserWindow
的一個配置項 - preload
,preload 容許你指定一段腳本在頁面加載以前載入,這段腳本你可使用 Electron 和 Nodejs 的 API,即便你在配置中不容許使用 Nodejs。
// preload 示例 var opts = { autoHideMenuBar: true, fullscreenable: false, webPreferences: { javascript: true, plugins: true, nodeIntegration: false, // 不集成 Nodejs webSecurity: false, preload: path.join(__dirname, 'preload/window_sdk.js') // 但預加載的 js 文件內仍可使用 Nodejs 的 API } } this.webviewWindow = new BrowserWindow(opts);
預加載 js 文件與其餘 js 並沒有二致,你只要根據你的業務,在 preload 的 js 中使用 remote 或者 ipc 通訊給你的 Web 應用提供夠用接口就行了。
Electron 並非很複雜,在寫完很少的主進程代碼後,其餘的業務代碼幾乎和 Web 應用沒什麼區別,甚至能夠將一個線上應用迅速的包裝成爲一個客戶端應用,好比electronic-wechat、worktile桌面端。
不過坑不可避免(好比沒法將 gif 寫入剪切板等等),有時候也會感到很難像 Native 那樣靈活,雖然如此,我仍是很欣慰能有這樣的工具,讓咱們前端能夠作更多的事情。
最後多說一句:雖然Electron 的進程間通訊很方便,並且支持多窗口,但我仍是傾向於使用 Electron 構建單窗口應用,相似網易雲音樂、Atom 等等,更簡潔,思惟方式上更像咱們熟悉的 web 應用。
小廣告
小前端FE博文的首發地址:http://blog.smallsfe.com
另外,歡迎咱們的微信公衆號:小前端FE(smallsfe)