Electron入門指北

最近幾年最火的桌面化技術,無疑是Qt+Electron
二者都有跨平臺桌面化技術,並不侷限於Windows系統。前者因嵌入式而誕生,在演變過程當中,逐步完善了生態以及工具鏈。後者則是依託於Node.JsCCM(Chromium Content Module),支持Node.jsNode.js原生以及自主封裝的Electron API
由於項目考慮跨平臺的技術選型,研究過長達一年的Electron,而且作了不少嘗試,因此想與諸君分享Electron的技術心得。javascript

Electron的特色

Electron依賴於Node.js,只要會用Node.js開發程序的,均可以用Electron開發桌面應用,只須要前端結合Electron API,就能夠快速完成桌面應用,一處代碼,多處編譯前端

Electron的優點

能夠編譯在Windows、Mac、Linux的X64/x86環境,由於是系統獨立打包,不依賴運行時
頁面開發依賴於Node.js加前端,能夠很便捷的採用前端開發頁面,再經過Electron API結合的形式,Electron API能夠理解爲一套Node原生庫,實際上安裝也就是一句 npm install electron
Electron內核採用Chromium,能夠兼容主流瀏覽器,並不須要額外適配,並且採用Chromium LST(長期支持版本),並不會過於激進的選擇最新版本,減小了前端爲了兼容適配帶來的風險性。
另外一方面,採用前端作UI層,能夠減小UI對於系統的適配狀況。java

Electron的應用場景

因爲採用了Node.js和前端的開發模式,自己是一個嵌套了瀏覽器的本地化前端應用,適合一些沒有底層操做的應用場景,甚至前端頁面能夠是遠程地址,這樣子徹底能夠完成桌面應用的無縫熱更新node

技術的侷限性

Electron採用的核心架構是Node.js,因此一旦涉及非頁面展現層的功能,好比要與com交互,要操做底層庫,好比各類硬件交互的SDK XX.dll,這裏就須要參考Node原生來編寫原生擴展了。web

Node原生編寫

參照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常見的雷區

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原生的編寫和實現。

相關文章
相關標籤/搜索