最近几年最火的桌面化技术,无疑是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原生的编写和实现。