最近幾年最火的桌面化技術,無疑是Qt+和Electron。
二者都有跨平臺桌面化技術,並不侷限於Windows系統。前者因嵌入式而誕生,在演變過程當中,逐步完善了生態以及工具鏈。後者則是依託於Node.Js和CCM(Chromium Content Module),支持Node.js和Node.js原生以及自主封裝的Electron API。
由於項目考慮跨平臺的技術選型,研究過長達一年的Electron,而且作了不少嘗試,因此想與諸君分享Electron的技術心得。javascript
Electron依賴於Node.js,只要會用Node.js開發程序的,均可以用Electron開發桌面應用,只須要前端結合Electron API,就能夠快速完成桌面應用,一處代碼,多處編譯。前端
能夠編譯在Windows、Mac、Linux的X64/x86環境,由於是系統獨立打包,不依賴運行時。
頁面開發依賴於Node.js加前端,能夠很便捷的採用前端開發頁面,再經過Electron API結合的形式,Electron API能夠理解爲一套Node原生庫,實際上安裝也就是一句 npm install electron
Electron內核採用Chromium,能夠兼容主流瀏覽器,並不須要額外適配,並且採用Chromium LST(長期支持版本),並不會過於激進的選擇最新版本,減小了前端爲了兼容適配帶來的風險性。
另外一方面,採用前端作UI層,能夠減小UI對於系統的適配狀況。java
因爲採用了Node.js和前端的開發模式,自己是一個嵌套了瀏覽器的本地化前端應用,適合一些沒有底層操做的應用場景,甚至前端頁面能夠是遠程地址,這樣子徹底能夠完成桌面應用的無縫熱更新node
Electron採用的核心架構是Node.js,因此一旦涉及非頁面展現層的功能,好比要與com交互,要操做底層庫,好比各類硬件交互的SDK XX.dll,這裏就須要參考Node原生來編寫原生擴展了。web
參照Node.js官網的原生擴展,編寫一個簡單的例子shell
執行npm init
建立一個默認的package.jsonnpm
如下代碼保存爲 hello.ccjson
#include <node.h> namespace demo { using v8::FunctionCallbackInfo; using v8::Isolate; using v8::Local; using v8::Object; using v8::String; using v8::Value; void Method(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8( isolate, "world")); } void Initialize(Local<Object> exports) { NODE_SET_METHOD(exports, "hello", Method); } NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) }
如下代碼保存爲 binding.gyp瀏覽器
{ "targets": [ { "target_name": "hello", "cflags!": [ "-fno-exceptions" ], "cflags_cc!": [ "-fno-exceptions" ], "sources": [ "hello.cc" ], 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ], } ] }
執行兩個命令 node-gyp configure node-gyp build
若是提示命令不存在,則 npm install node-gyp
命令很簡單 前者是生成項目文件,後者則是編譯文件,生成 hello.node ,至於爲何叫hello.node,能夠參考 binding.gyp/targets/target_name前端框架
如下代碼保存爲 index.js
var addon = require('./build/Release/hello'); console.log(addon.hello()); // 'world'
運行 node . ,顯示
好像一切都挺順利的
按照Electon的例子,編寫index.js
const { app, BrowserWindow } = require('electron') function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) win.loadURL('https://electronjs.org') var addon = require('./hello'); console.log(addon.hello()); // 'world' } console.log(process.versions.node); app.whenReady().then(createWindow);
代碼自己能夠運行起來,可是加入了調用Node原生以後就提示
這明顯不能用啊,並且很認真的保持了Electron內部Node.js和外部編譯插件的Node.js的一致性
參考了electron官方例子未果後,反覆搜索資料,一一驗證,最後發現,要在node-gyp編譯的時候,標註版本
node-gyp configure --target=版本號 --dist-url=https://atom.io/download/atom-shell
node-gyp build --target=版本
看見了node-gyp,你覺得是編譯的node對應版本的插件?no,這裏編譯的是electron對應的版本
程序運行成功的那一刻,我差點口吐芬芳,不帶這麼坑人的
程序也跑起來了,是否是 「天晴了雨停了,感受本身又行了」
這才第一步呢,實際上node.js原生的寫法是專屬寫法,帶有大量的侵入式的插件設計,這塊不亞於重寫學一門C語言的亞種 (好比Object-C和C的差異)
這還只是本身撰寫原生擴展,若是是海量API組合的呢?這個擴展要寫成什麼樣?所幸社區開發了一種神器,叫FFI
此時應有掌聲
你覺得這裏是爲你鼓掌的?不,這只是開啓了探索星辰大海的第一步,告訴你,不要哭,將來哭的日子還長呢。
FFI的編譯文章千千萬,編譯不成功的文章更是數不勝數,沒有一篇資料闡述了FFI與Node之間晦澀不明的版本關係 ,也沒有一篇文章告訴你,如何成功安裝。
成功的都是僥倖,失敗,那纔是人生。
小夥計,你已經上了黑車,車門焊死了。
Electron的瀏覽器內核叫 CCM(Chromium Content Modult),是否是有CEF小夥伴已經嗷嗷叫了?這個自帶了mp4支持,不用再編譯了,是否是更興奮了,幸福來的太忽然?
先別忙,瞅一眼Electron API,你品,你細品。
是否是沒有CEF常見的各類擴展Handler?沒錯,CEF重度產品,至少在默認的Electron框架上,是不少作不了的,好比截取瀏覽器區域的webgl renderer/render的產品,好比各類音樂播放器,基於音頻的Handler二次混合疊加的產品,在默認實現上,作不了。
常見的一些前端小組件被移除,常見的好比localstroge、sessionstroge不可用,須要搜索一些三方的組件替代性使用,好比electron-localstroge。
常見的Window.Open被修改,實現指向了Electron.BrowserWindow。
Electron自己的發佈其實沒有什麼問題的,一旦引入了原生擴展,發佈的時候,就須要當心的從新編譯Electron再發布,否則會出現某知名在線教育機構的Electron桌面應用,發佈到客戶電腦上,報錯。
咳咳咳,具體是哪家,就不提了,咱也不知道,咱也不敢說。
寫到這裏,只是想給對Electron感興趣的小夥伴,一個冷靜的分析,本身所在的團隊,是否能撐得起這套框架的成本,看得見的便利性是存在的,看不見的隱藏成本,是否能夠爲此買單。 確定會有不少微軟狂熱粉 VS Code也用的Electron啊! 實際上對於那種技術研發的大型公司,用什麼技術,已經再也不是難題了,選擇了Electron,也只是爲了擁抱社區作的妥協,並不表明這個技術的選型是最佳方案。 技術的選擇依舊是 本身的團隊是否能夠承擔簡單功能外的Node原生的編寫和實現。