得物商家客服桌面端Electron技術實踐

語言: CN / TW / HK

1.業務背景

隨着公司業務的快速發展,商家客服也納入了我們的服務範圍,商家客服工作台的定位是通過工具和數據服務商家,一站式解決用户購買諮詢訴求。通過工具和運營策略協助商家提升服務品質,讓品牌商家有動力運營好潛在的客户,從而達到提升用户服務的目標。桌面應用的轉化在未來是客服產品的方向。

  • 已有web端聊天系統的前提下,商家客服為什麼要遷移桌面應用?

首先我們收到部分商家客服反饋:

640.png

  • 用户是上帝,我們是很重視用户的反饋的,所以首先我們想的是如何在web端解決這些問題,下面我們逐一分析下以上問題我們能不能在網頁端解決呢?

針對客服A同學問題:大多數客服職場的台式機是不會安裝音頻設備,如果人家沒音頻,沒外音,我們能強迫他買個播放器嗎,那肯定是不能的,如果是自營客服還有點處理方案,真需要,公司可以統一採購,但是ToC的顯然不能強制做什麼事情,所以此問題無解。

針對客服B同學問題:這句話怎麼理解呢,是這樣的,在一屏既有web瀏覽器,又有其他應用如飛書之類的PC應用疊着放,web notification通知由於是瀏覽器級別的,提醒不到最上層,瀏覽器的提醒確實有點弱(看不到提醒會影響到客服的首響,首響會影響客服的績效,咱公司對於用户的服務效率是比較嚴格的),所以此問題無解。

針對客服C同學問題:em。。。好的,您説的對,web網頁給商家客服的感覺就是我們平台有點趕不上形勢。。。

基於上面的一些場景,想必大家已經對為何做桌面應用有個初步的瞭解,下面以一張圖來看下web應用跟桌面應用的區別。

6401.png

2.技術選型

  • 為什麼會選擇Electron而不是其他應用開發框架?

2.1 Electron架構簡介

6402.png

Electron的構成主要是上面的3個大模塊,每個模塊各司其職,讓Electron有了桌面應用的能力。

  • Chromium:可以為Electron提供UI渲染能力,再加上Chromium本身就是跨平台的,所以也不用考慮代碼的兼容性。只要有Chromium,就能用JavaScript,HTML,CSS這些前端開發工程師所熟知的三劍客來開發頁面。

  • Node.js:Chromium並不具備原生GUI操作的能力,所以Node.js正好補足了這個能力,能夠調用操作系統的底層 API,比如path、fs、crypto 這些模塊,甚至能集成C++。

  • Native APIs:Native API讓Electron有了跨平台和桌面端的原生能力,比如説它有統一的原生界面,窗口、托盤這些。

2.2 Electron與其他框架的區別

下面是Electron與Native、QT、NW應用的對比圖:

![截屏20230131 15.49.19.png](https://cdn.poizon.com/ctoo/013115/截屏2023-01-31 15.49.19.png)

  • Native(C++/C#/Objective-C)不管從原生體驗、包的體積、性能方面來説都是最佳的選擇,但是開發門檻高,迭代速度慢。

  • QT是基於C++的跨平台開發框架,跨平台應用十分廣泛(Mac、Windows、ios、Android、Linux、嵌入式),眾所周知的WPS就是用QT開發的。性能很好,甚至於可以媲美原生的體驗,但是整體門檻還是比較高的。

  • Web技術的代表Electron 和 NW.js ,相比之前選擇Electron,Electron有非常活躍的社區,有102k star,有Atom、vscode這樣的大型應用都是基於Electron開發的,性能相比於natvie是肯定要差一些,但綜合來看,Electron是作為開發桌面應用的目前首選。

  • 值得一提的是Flutter desktop,從渲染原理看flutter是skia自繪性能優於Electron,但問題還是穩定性和生態。Electron由於是nodejs+chromium,前端的生態可以直接用,所以Flutter desktop就不納入考慮範圍的,但值得關注。

除了以上這些,最主要的一點:Electron能快速交付,業務層複用web系統的代碼,只需要關注主進程就好了,並且也能滿足業務需要的系統級別的一些功能。

6403.png

3.技術實現

3.1 項目架構

6404.png

首先介紹下Electron框架裏面兩個重要的概念主進程和渲染進程。

主進程:主要負責創建和管理BrowserWindow實例以及應用程序事件。它可以執行註冊全局快捷方式,創建系統菜單和對話框,響應自動更新事件等操作。主進程以及所有Node.js模塊中都提供了一部分Electron API。

渲染進程:渲染過程負責運行應用程序的用户界面,渲染進程中提供了所有DOM API,Node.js API和Electron API的子集。

6405.png

如上面截圖,打開Electron項目之後會有多個進程,一個項目有且只有一個主進程,創建窗口等有關係統事件寫在主進程中進行,但是渲染進程可能有多個。

那為什麼會有多個渲染進程呢?

Electron應用是Chromium內核,所以多進程的架構也來源於Chromium,Chromium會單獨運行每個標籤,任何一個標籤頁崩潰了都不會影響到其他標籤。因此,每個進程在自己的空間中運行,由操作系統調度。如果某個進程觸發了無限循環,也不會導致整個應用down掉。

在上文説到兩個字“遷移”,是的,我們在開發桌面應用之前有非常成熟的web端商家客服聊天系統了,所以我們在開發桌面應用的時候大多數時候是關心主進程的,渲染進程並不太需要關心。

3.2 主進程功能模塊

3.2.1 通信模塊

主要是調用Electron框架本身的API以及通用方法的封裝。

6406.png

主進程到渲染進程的通信:

640.jpg

渲染進程到主進程的通信:

有兩種方案,一種是在主進程開啟了nodeIntegration: true之後在渲染進程裏面可以使用window.require('Electron')來引入寫通信相關代碼

6401.jpg

一種是需要在主進程編寫preload.js,在初始化窗口的時候引入

6402.jpg

通信的同步和異步問題

  • 【異步】渲染進程->發送->主進程

6403.jpg

  • 【同步】渲染進程->發送->主進程

6404.jpg

3.2.2 菜單模塊

主要是調用Electron框架本身的API,滿足快速擴展菜單功能以及自定義菜單功能。

6405.jpg

但是商家客服項目沒用到原生菜單,而是用了自定義的菜單。

沒有使用原生菜單的主要原因是:

  • 原生菜單樣式較死板,不能調整;

  • 自定義編寫菜單能有各種自己想要的功能,可控制其展示。

3.2.3 托盤模塊

托盤屬於系統級的操作,所以是在主進程中設置的。在開始之前需要注意的地方,設置托盤必須在程序ready之後。實現較簡單,代碼如下:

6406.jpg

3.2.4 異常處理模塊

主要調用Electron框架本身API,結合Node.js API,檢測系統異常後自動刷新並上報,渲染進程的異常已經使用sls&arms處理,主進程的異常主要是通過Electron的crashReporter API來記錄日誌。

6407.jpg

上面提交參數有幾個需要注意的點:

  • submitURL 是以post方式上傳

  • extra 一個你可以定義的對象,附帶在崩潰報告上一起發送 . 只有字符串屬性可以被正確發送,不支持嵌套對象

3.3 渲染進程功能模塊

渲染進程的代碼大部分跟商家客服web端一致,很多隻是遷移即可。

3.3.1 登錄改造

登錄信息本地化,在登錄成功的時候,把賬號信息緩存,下次打開應用的時候客服無需再重新輸入,直接從緩存獲取即可。

6407.png

3.3.2 靜態資源

傳統Web應用,將項目代碼部署服務器,項目運行時,訪問的是服務器靜態資源,現在版本發佈流程,走的是cdn資源,總而言之都是通過網絡獲取。

6408.png

Electron提供將靜態資源打包到安裝程序,在安裝時,將項目文件同步安裝到用户電腦,使其具備訪問本地文件,減少了請求佔用資源,一定程度上也能改善因網速問題導致的靜態資源不能實時獲取,頁面白屏問題。

6408.jpg

3.3.3 數據存儲

Electron應用裏面的數據存儲是通過Electron-store第三方庫來實現的,實現比較簡單,如下:

6409.jpg

3.3.4 渲染進程打包

這塊為什麼要單拎出來講渲染進程打包呢,是因為web項目遷移變成應用渲染進程的時候不能像web應用一樣直接打包,需要調整請求API代碼,API前綴需要區分本地調試和應用環境:

6409.png

使用Electron, 將項目打包成離線應用。使用file協議,在本地讀取靜態資源。但是ajax請求如果用相對路徑,打包之後,會直接找到根目錄,如下截圖:

64010.png

所以打包的時候需要給ajax提供完整的url路徑。

4.技術挑戰

在從0到1搭建商家客服桌面端的過程中,遇到了很多的問題,Electron社區雖然很活躍,但是不一樣場景遇到的問題,幾乎找不到對應的解決方案,所以很多都是在探索過程中不斷的去完善。這裏主要圍繞發佈構建流程和安全性來講下,我們是怎麼解決的。

4.1 安全性問題

Electron客户端的安全問題也是非常重要的,那都遇到了哪些安全問題以及我們又是如何解決的呢,具體如下:

  • 渲染進程XSS:Electron實現的桌面端軟件渲染層的原理實際是通過chrome內核渲染的,同樣存在XSS注入的風險。舉個例子:在html頁面中可以執行命令:<img/src=x οnerrοr="require('child_process').exec('gnome-calculator')"> ,就可以打開當前操作系統的計算器。接入了公司統一的XSS治理方案,該問題即得到解決。

  • 用户認證信息泄漏問題:商家客服桌面端登錄調用商家的授權接口,APP網關有校驗,可以確保登陸沒有問題;

  • 本地緩存明文讀取問題:本地數據泄漏,例如:indexDB、localStorage、sessionStorage等,我們主要用加密和解密算法對本地緩存信息進行處理。

沒有絕對的安全,我們能做的就是儘可能的提高安全門檻,過程中我們也積極同公司的安全部門進行溝通,讓他們排查桌面端發佈之後的安全漏洞,最終驗證都是滿足安全標準,符合發佈的條件。

4.2 發佈構建流程

應用發佈涉及到渲染進程和主進程,渲染進程主要是負責給主進程提供渲染包,主進程使用Electron-builder庫來打包部署所發佈的包。

64011.png

前面已經説過,Electron的好處是可以無縫集成web端的業務邏輯代碼,這裏上圖左邊紅色的是web端構建出的產物,我們會把這部分構建產物同步到主進程的app/render目錄下,即渲染進程目錄,這樣在打包應用包的時候,就能集成渲染進程的業務邏輯,而不需要維護兩份web端的代碼。

此方案還存在不少的缺陷,由於生產構建環境需要window環境,所以暫時不支持在遠程打包,目前都是在本地window機器上打出完整的包之後再上傳到CDN,商家客服通過加載CDN的更新包來替換本地安裝文件,實現軟件的本地安裝。

4.3 應用更新問題

應用開發離不開“更新”這個話題,比如飛書應用會時不時彈出一個更新窗口,讓你選擇是否更新,商家客服在推廣桌面應用之後,也存在更新這個問題。在業務快速發展的同時,如何將業務需求更好的同步給商家使用,這是商家客服桌面應用面臨的最大的挑戰。

4.3.1 全量更新

4.3.1.1 手動下載安裝

最基礎的更新模式,主要思路是在打開app(其他時候也行,我們業務主要是打開app的時候)的時候訪問遠程的json文件,文件內容包含版本號,更新內容等等最新版本的信息,拿到遠程版本號會跟本地應用版本號做比較,如果版本號不一致,就詢問用户是否更新,需要更新的話會下載到本地,用户手動點擊安裝即可。這個更新方式不推薦使用,如果你的應用一年更新一次,ok,是可以這麼做的。

64012.png

4.3.2 增量更新

在網速快的情況下,全量更新跟增量更新幾乎是沒有區別的。但是網速慢的情況下它倆之間的差距會被放大,用户體驗不是很好。我們不能想當然的以為所有用户網速都很好,這是不現實的,所以不管是PC應用還是移動端應用,大多數情況下是需要做增量更新。下面表格是網速不一樣情況下的下載耗時對比:

WeChate37b6a37cfe5899d3d525368ea1b1270.png

4.3.2.1 electron-updater

現在就開始介紹在商家客服應用(windows應用)中是怎麼實現增量更新功能的。

更新在大的分類上區分全量以及增量更新,在每個小分類裏面也區分強更新,弱更新(業務上的區分,底層實現沒區別)。簡單來説,強更新指的是用户必須更新,不更新將無法使用系統功能,弱更新指的是用户想要的時候再去觸發應用的更新,完全由用户自主選擇。

64013.png

更新流程

64014.png

其中electron-updater作用於“更新應用”這個節點,主要是依賴新舊版本blockmap文件的對比來實現增量更新。下面截圖為electron-builder打包出來的release包,每次打包都會有對應的blockmap文件。

64015.png

electron-updater更新實現主要流程:

  • 生產的blockmap文件:

1.使用7z壓縮安裝包

2.讀取安裝包的header

3.計算出每個file的offset和end得到相應的hash生產blockmap

  • 使用blockmap文件:

1.下載雲上的blockmap文件跟本地blockmap文件對比,從上面截圖可以看出blockmap文件很小,所以下載並不會對應用性能產生影響

2.使用range,request(範圍請求)請求更新內容的部分

4.3.2.2 文件替換

還有一種增量更新方式就是文件替換,只更新需要更新的模塊,這種方式只更新需要渲染進程的資源,大部分情況下主進程的資源不用更改,所以下載的資源會比較小,更新較快,因為是在線熱更新,更新完成後不用重新啟動軟件,只需要刷新頁面重新加載資源即可。其實之前我覺的這樣的思路挺好的,看下面的流程圖也是可以實現的,也很符合商家客服桌面應用產品需求。

64016.png

可是後來發現其實忽略了以下兩個點:

  • 替換用户本地文件這個本身有權限問題,比如windows用户安裝到了C盤,寫入文件是有管理員權限限制的;

  • 文件被佔用問題,眾所周知,當文件夾中存在正在被佔用的文件時,刪除會失敗。所以在覆蓋原文件同時需要退出應用避免佔用,所以這個方式也不是很可靠。

5.遇到的問題

Electron 的硬件加速功能,在 win7 或者 Linux 系統上,容易出現黑屏或者卡死。

解決方案:判斷是不是win7及以下系統,如果是app.disableHardwareAcceleration (),禁用當前應用程序的硬件加速。

  • Uncaught ReferenceError: require is not defined,這個報錯是試圖在渲染進程使用node的時候出現的,不是不能用,只要開啟 主進程的nodeIntegration: true就好了, 但不建議,有安全問題。

解決方案:

64010.jpg

  • Note:you may have one or two (large) stale temporary file(s) left in your temporary directory (Generally this only happens on Windows 9x)這個是打包了半天都打不出來一個完整的包的情況下出現的。

解決方案:當時是因為我沒刪除原來的包導致我放打包文件的C盤滿了。。。所以刪除一些緩存就好了,nsis打包大概率都是跟磁盤有關。

  • 下載npm包特別慢

解決方案:yarn安裝;Electron相關的包優先使用淘寶鏡像安裝;使用公司鏡像安裝公司內部包。

6.總結

一路開發下來,感慨很多,作為公司第一個Electron應用,不管是在開發上,打包上,或者説在部署上,都遇到了一些挑戰,在網上也沒有比較詳細的文檔,外面做的好的也不會把詳細方案分享出來,但是即使遇到了這些問題,也不能否認Electron是目前最適配於我們業務目標以及適配於開發資源的一個框架。目前已有線上穩定版本,逐步在推廣到全部商家客服。接下來需要完善的開發流程,克服的技術難點有很多,商家客服工作台應用也會越來越完善。

文/Uni