electron 和 tauri 都不会用,那就自己写个吧

electron 和 tauri 都不会用,那就自己写个吧

前语

在《屠龙少年终成恶龙,前端转产品的我给前端挖了个坑》这篇文章里,有讲到我是如何把咱们的前端带坑里去。一起谈论区有一条谈论 你那个demo.exe是用什么完结的?能够直接套壳现有的体系? 看起来如同是在坑外徜徉不定若有所思的姿态。因此我打算写本文把他踹坑里去,能踹一个是一个。

不想用 electron 和 tauri ?那咱们一起来写个像 electron 的垃圾玩意吧~ 咱们的方针是:前端程序员无需会三方言语就可独立完结桌面程序,创立托盘程序和服务、读写文件、处理进程、剪贴板这些都没有问题,估计体积1M内,最大不超过2M。

为什么做

我当时现已运用 nodejs 开发一个命令行程序,这个程序的东西办法是,从网络上获取动态的装备,然后读取这个装备进行发动。发动后就能去做其他额外的工作了,而不需求管这个程序。由于这个程序仅仅一个辅助东西。

可是现在有一些痛点:

每次发动的时候我都得先找到项目目录,然后运转 node xxx.js,然后发动一个黑框框,然后我再最小化这个框。发动进程相当费事而且有一个不必管的窗口在任务栏,相当碍眼。

有许多办法能够处理发动问题,比方 pm2/快捷链接/大局装置/制作 PKG 装置包等。但各自有各自的问题,这儿不一一列举。

关于有一个黑框需求最小化到任务栏问题,我测验过运用 node child_process 的 detached=false, windowsHide=true 等参数合作 pm2 都是没有用的,黑框仍是会弹出。 假定有用,我要的也不仅如此。

我觉得这个东西不错,我想要把这个东西发给他人运用,尽管这个东西是 nodejs 写的,但我不希望他人还要去学习装置 nodejs 环境。尽管这个东西是命令行发动,并支撑参数装备,但我希望像惯例程序相同,他人点击一个图标就能发动,能够从界面上装备参数。能够在界面上看到程序的实时日志,最小化之后,变成一个小图标在任务栏,不占空间不碍眼。

那么问题来了,由于我经常用 html/css/js 画界面,对许多前端组件库比较了解,所以我打算用前端写界面。但 js 是跑在浏览器里的,读取不了保存在电脑里的装备文件,更完结不了托盘图标功用,也运转不了 node 程序。

据我所知,像这种想运用前端言语开发界面,又需求与操作体系进行交互的功用,有不少方案。下面是我对他们的调研成果:

名称 前端 后端 体积 MB 内存 MB 抛弃原因 补白
nodegui chromium nodejs 100 100 体积大
miniblink49 Chromium nodejs ? ? 体积大 仅支撑 window
NW.js Chromium nodejs 100 100 体积大
electron Chromium nodejs 100 100 体积大
Wails webview go 8M ? 需其他言语
Tauri webview rust 1 ? 需其他言语
Qt 可选 C++ 30 ? 需其他言语
wpf 可选 C# ? ? 需其他言语 仅支撑 window
Muon Chromium go 42 26 需其他言语
Sciter Sciter QuickJS 5 ? 与一般浏览器和 nodejs 可能有差异
gluon 浏览器 nodejs 1 80 生态小,例如没有找到托盘图标完结办法
neutralino 浏览器 API 2M 60 api 不多

当时咱们比较火有 electron 和 tauri。四年前我运用过 electron 做过一个桌面划词程序,由于涉及到体系操作,所以需求装置 node-gyp/pytohn/visual studio 等依靠来进行本地编译,能否操作成功与 electron/node/node-ffi 等版别兼容性有很大的联系,装置进程和 electron 的体积都给我留下了欠好的印象,另外 electron 里的主进程、烘托进程、通讯的一些运用上的差异,也让我觉得不那么便利,所以我抛弃 electron 。

接下来就是 tauri,它由于不打包 nodejs 和 chromium ,所以体积较小。但我看他官网上的 demo,就连发动都 rust 代码。

electron 和 tauri 都不会用,那就自己写个吧

尽管代码没几行,但我也是相当回绝:说好的只运用前端言语就能写桌面程序呢?

所以我抛弃了 tauri 。原因是我真想找一个不运用三方言语就能做桌面程序的东西。我发现 neutralino 比较贴近我的需求,但它当时还很年轻,许多 api 和示例都没有。这相当于假如遇到了操作体系层面上的问题,只需他不供给 api 我就没法操作,由于我不会写原生代码,所以又抛弃了 neutralino 。

所以就自己做一个吧。

预备怎么做

预备运用当时了解的一个言语做一个基于 webview 的东西,咱们暂且叫 main。它加载好前端页面,并向前端页面注入 api 并连接上 websockets 。假如前端有什么对体系操作的诉求,告诉 main 即可,由 main 完结,关于前端而言,就像调用一个一般的 js 办法相同,传参、处理成果、完事。

言语名为 aardio ,由于“各自原因”这儿不做过多叙述。后边文档中统一称其为体系言语。

那么为什么都去搞一个言语了不搞 rust 这些?有几点考虑:

  • 供给了 js/webview/nodejs 相互调用的例子
  • 供给了一些常见的体系托盘、窗口操作示例等
  • 我对作者保护这个言语这么多年心存敬畏

程序的全体架构是这样的:

  • 装备层:常用的定制化需求,都能够经过一个 json 装备文件解决。js处理起来也简略。
  • 依靠层:比方注入到 web 页的经过封装的 js 文件。
  • 内核层:完结与 web 页面的通讯,满足 web 页面对体系进行操作的惯例诉求。
  • 东西层:例如健壮性、安全性、主动晋级、调试、打包、发动等。

做成了什么样

下面这个图片演示了发动程序时,有一个绿色的进度条(不会遮挡鼠标),然后进入界面。

现在已过可行性验证阶段,给客户做了一个文件办理体系程序,相似一个网盘,页面由前端完结,然后文件的下载、预览、同步这些交给 main 供给的 api。

下面这个图片演示了在 web 中关闭程序。

关于自己的话,做了一个 ai 助手,对接的开源 ai-ui,已发给同事运用,也没有问题。做了一个文章开关说到的助手程序,自己运用。

再次演示一下通明窗口,上面的发动时的进度条也是运用通明窗口完结的。

electron 和 tauri 都不会用,那就自己写个吧

演示自定义窗口标题和托盘。

electron 和 tauri 都不会用,那就自己写个吧

程序发动时的进度条也是运用 html 完结的

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>loading...</title>
    <style>
      body,
      html {
        height: 100%;
        overflow: hidden;
      }
      body {
        display: flex;
        align-items: center;
        justify-content: center;
      }
      @property --progress {
        syntax: '<percentage>';
        inherits: false;
        initial-value: 0%;
      }
      .g-progress {
        margin: auto;
        width: 240px;
        height: 10px;
        border-radius: 25px;
        background: linear-gradient(90deg, #0f0, #0ff var(--progress), transparent 0);
        border: 1px solid #eee;
        transition: .3s --progress;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div id="progress" class="g-progress progress-bar-striped" style="--progress: 10%"></div>
    </div>
  </body>
</html>

遇到的问题及处理方案

官方示例中给到的 webview 交互示例经过 external 注入到页面的 window 上,经过此办法能让 js 中的数据和 main 进行类型转化(比方 js 里传一个 number,那么到 main 里也是 number),还供给了一些能够直接启用 main 里目标办法的操作。好用是好用,可是与 nodejs 交互的时候,没有这种主动转化的功用,而且示例中的 node 服务连接很慢。

为了让 main 支撑 webview 和 nodejs,而且运用办法统一,而且加快发动速度,查了一些材料,发生像这种跨言语通讯通常都是运用 rpc 协议完结的,有 json-rpc/http-rpc/rpc-ws 等,为了实时性更强,我挑选了 websockets 这种办法, 我 npm 社区中发现有 www.npmjs.com/package/rpc… 这个包可用,还兼容 node 和浏览器,测验往后挑选了它,这解决了跨言语通讯问题。

另一个问题是,mian 中有许多办法是现成的。比方以下代码在 main 中能够运用:

// 有一个 winform 目标
winform.hitMax() // 最大化
winform.show() // 显示窗口
winform.hwnd // 获取窗口句柄
winform.hitCaption() // 拖动窗口
winform.text = "title" // 设置窗口标题
// ... 上百个现在的办法和特点

假如咱们要为 js 供给 api, 咱们是每个特点和办法都得去写吗?这又费事,代码又还臃肿。

经过一波挣扎,我想起了运用署理这种办法去完结,仍是 js。

const obj = new Proxy(
  {},
  {
    get: function (target, key, receiver) {
      console.log(`getting ${key}!`);
      return Reflect.get(target, key, receiver);
    },
    set: function (target, key, value, receiver) {
      console.log(`setting ${key}!`);
      return Reflect.set(target, key, value, receiver);
    },
  }
);

依据 proxy,咱们能够完结拦截到某个目标的办法调用和特点访问、设置等。再加上深层署理的话,像 winform.process.close() 这种有任何层办法特点都没有问题。

一起,在 main 中咱们有这样的代码,来处理 proxy 拦截到的每个 key path:

electron 和 tauri 都不会用,那就自己写个吧

咱们把拦截到的 path ,比方在 js 里写 winform.process.close(true) 的时候,咱们把拦截到的 winform.process.close 和参数 true 经过 rpc-ws 供给的 call 办法传给 main,这时候 main 依据 path 去动态调用函数并把参数传进去。咱们把履行成果又丢给 call 办法回来给咱们的 js 即可。

那么问题又来了,既然都完结了在 js 里调用办法和访问特点都像在写 main 中的代码相同,那真的就能不能以 js 的方式去写 main 的代码呢?看了一天的教程,发现这水很深啊,约等于发明一门言语,怕了怕了,逃。

可是思路着要有吧?好的:

假如简略一些呢,咱们依然能够运用 proxy,完结操作符的拦截,从而完结一些简略的加减乘除的操作。然这没什么用啊,咱们要完结的是比方用 js 里对 winform 目标进行遍历之前,咱们就要做一个生成器之类的东西,在生成器的每一步里,去获取 main 里的遍历成果。感觉上如同能完结,实际我也不知道我在说什么。可是就算完结了,像这种遍历器,频频的言语交互应该会耗费很多时刻,感觉应该因小失大。

所以在 js 里获得 main 中言语的编写体会,就不完结啦。假如咱们真的要在 js 里写另一种言语,咱们开放一个相似 js 的 eval 的功用。它能够向 main 传原生代码和参数。

// 创立目录
const dir = `C:/my/`
await ws.call(`run`, [
`
  fsys.createDir(arg)
`, dir])

例如上面这段代码,直接传送目录参数 C:/my/ 到 arg,运用原生言语 fsys.createDir(arg) 去履行。

后期方案是什么

方案一:运用 main 去做更多的桌面 app,以此促进 main 的完善。

方案二:为某个当时老练的 ui 框架拟定一套 css 皮肤,例如 win7 皮肤 ,例如 element-ui 姿态很 web,但应用了这个皮肤之后,全体页面风格和控件都看起来就像原生 win7 桌面程序相同。

electron 和 tauri 都不会用,那就自己写个吧

方案三:赶快完结 api 的封装和文档,让前端朋友只调用指定的 js api 即可完结托盘、进程、剪贴板、IO等体系操作。咱们封装的 api 尽量向 neutralino 接近,做到最小成本的迁移。等它老练在,能够迁入,没老练之前咱们也能自己用着。

需求什么协助

能够帮咱们封装 api,这需求你了解 main 的言语;能够用 main 来做些小东西测验一下,这就是最好的协助;能够做操作体系风格皮肤,等你做好了,electron 和 tauri 他们都能用,由于他仅仅 css;或者能够点个 star github.com/wll8/sys-sh…

好了,饼画了,牛吹了,坑挖了,我要去玩了。