近些年来,跨平台跨端一直是比较热门的话题,Write once, run anywhere一直是开发者所期望的,跨平台方案的优势十分明显,对于开发者而言,可以做到一次开发、多端复用,一套代码就能够运行在不同设备上,这在很大程度上能够降低研发成本,同时能够在产品效能上做到快速验证和快速上线。如今跨端跨平台的优秀技术方案也比较多:
泛指编程语言、软件或硬件设备可以在多种操作系统或不同硬件架构的电脑上运作。一般来说,有这几种场景,分别是跨设备平台(如 PC 端和移动端),跨操作系统,(移动端中分Android,IOS,PC端中分 Windows,macOS,Linux),国内的小程序(微信,京东,百度,支付宝,字节跳动等等)。
3.1 NW.js
官网:https://nwjs.io/
GitHub:https://github.com/nwjs/nw.js
语言:Nodejs + 前端任意框架
代表项目:微信小程序 IDE,京东小程序 IDE
NW.js(node-webkit )是一个基于 Chromium 和 Node.js 的 Web 运行环境,可直接在 DOM 中调用 Node.js 模块,并可使用任何现有的 Web 技术来编写本地应用。
图2 Electron 官网描述
3.2 Electron
官网:https://www.electronjs.org/
GitHub:https://github.com/electron/electron
语言:Nodejs + 前端任意框架
代表项目:VSCode,百度小程序 IDE,京 ME,Facebook Message,
Electron 的前身叫做 Atom-Shell,本来是 GitHub 发布开源编辑器 Atom 时一并发布的副产物,但是后来这个副产物的影响力远远的超过了 Atom 本身,于是便改名为一个独立专案,也就是现在的 Electron。Electron 的本质很简单,就是 Chromium + Node.js 的组合,两者之间通过 IPC 通讯。
类似于 NW.js,表面上,它们似乎非常相似,但是这两个项目有本质上的区别,使得 Electron 和 NW.js 成为两个完全独立的产品:
应用入口不同,NW.js 主入口是一个 HTML,Electron 中是 JavaScript,可操作性更强;
Node 集成方式不同,在 NW.js 中网页中的 Node 需要通过给 Chromium 打补丁来实现,Electron 则是通过各个平台的消息循环与 libuv 的循环集成,避免了直接在 Chromium 上做改动;
图3 Tauri 官网描述
3.3 Tauri
官网:https://tauri.app/
GitHub:https://github.com/tauri-apps/tauri
语言:Rust + 前端任意框架
代表项目:仅有少量开源应用
Tauri 是 2021年 JavaScript 明星项目的最受欢迎项目中排名第5,在 stateofjs 2021 中 满意度和关注度排名第1, 由于 Vite,esbuild,swc,Rome 等工具的大火,让基于 Go、Rust 的高效率构建类工具进入爆发期,加上 Bundleless 的构建体验,让 Rust、Go 成为前端开发者的又一扇门。由于 Tauri 的 Rust 背景,加上构建产物小,内存占用低,还是值得长期关注的。
图4 Tauri 在 JavaScript 明星项目和 stateofjs 中的排名
题外话:Rust 前景还是非常不错的,如 Linux内核接纳 Rust,deno采用 Rust,微软拥抱Rust,fuchsia 的 Rust 代码占比超50%,Apple 在底层 all-in rust,连续6年的 stackoverflow 最受欢迎语言,但学习门槛较高。
图5 Wails 官网描述
3.4 Wails
官网:https://wails.io/
GitHub:https://github.com/wailsapp/wails
语言:Go + 前端任意框架
Wails 是一个可让您使用 Go 和 Web 技术编写桌面应用的项目。可以将其看作为 Go 的快并且轻量的 Electron 替代品;可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。与 Tauri 类似,Windows 上使用的是 Webview2。
图6 Flutter 官网描述
3.5 Flutter for Desktop
官网:https://flutter.dev/multi-platform/desktop
GitHub:https://github.com/flutter/flutter
语言:Dart
本节将讨论使用较多的 Electron 和较有前景的 Tauri 的架构原理。
图7 Electron 架构组成
通过 Web 技术写 UI,赋予了底层能力,达到跨平台的能力及体验。
4.1 Chromium 多进程架构
大多数现代 Web 浏览器都为多进程架构,主要有浏览器主进程、渲染进程、插件进程、网络进程、GPU 进程,Chromium 也是如此。
IPC = Inter-Process Communication 进程间通信
图8 Chromium 架构图
Chromium 是 Chrome 的开源版,同时也是一个浏览器。
主进程的 RenderProcessHost 和渲染进程的 RenderProcess 负责处理 IPC 事件;
渲染进程的 RenderView,页面的展示会在这里基于 Webkit 排版出来;
ResourceDispatcher 处理资源请求,当页面需要请求资源时,通过 ResourceDispather 创建一个请求 ID 转发到 IPC,在 Browser 进程中处理后返回。
4.2 Electron 架构
图9 Election 架构图
在每个进程中暴露了 Native API(Main Native API,Renderer Native API)
引入 Node.js
Web 技术实现 UI
4.3 Electron 进程模型
Electron 继承了来自 Chromium 的多进程架构,这使得此框架在架构上非常相似于一个现代的网页浏览器。
为何采用多进程架构?
网页浏览器是个极其复杂的应用程序。除了显示网页内容的主要能力之外,它还有许多次要的职责,例如:管理众多窗口 ( 或 标签页 ) 和加载第三方扩展。
在早期,浏览器通常使用单个进程来处理这些功能。这种模式虽然能减小打开每个标签页的开销,但也同时意味着一个网站的崩溃或无响应会影响到整个浏览器。
为了解决这个问题,Chrome 团队决定让每个标签页在自己的进程中渲染, 从而限制一个网页上的有误或恶意代码可能导致的对整个应用程序造成的伤害,然后用单个浏览器进程控制这些标签页进程,以及整个应用程序的生命周期。
图10 多进程架构
Electron 也是如此,作为应用开发者,控制着两种类型的进程,主进程和渲染进程:主进程负责应用程序窗口管理,应用程序的生命周期,原生API等;渲染进程负责UI的展示,这部分可以选择任意前端框架 Vue、React、Svelte、Preact。
4.4 Tauri 进程模型
Tauri 采用了一种类似 Electron 和大多数现代Web浏览器那样的多进程架构。包括主进程和 WebView进程,单个主进程管理一个或多个 WebView 进程。
图11 Tauri 进程模型
4.5 进程间通信
Electron 的进程通信:渲染器进程 -> 主进程
(双向)ipcRenderer.invoke 与 ipcMain.handle 搭配使用来完成
(单向)ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收
为了对比 Electron 和 Tauri 差异性可以分别用 Electron 和 Tauri 做一个简单的应用,导航启动器,类似 Alfred,Spotlight。
5.1 功能描述
首先来描述下这个应用的功能,启动应用后,通过快捷键 Ctrl/Command + K 唤起应用界面 - 一个输入框,在输入框输入关键词 git 会展示 git 相关的系统名称列表,选择后回车即可打开 github.com,相当于另类的书签。
5.2 设计思路
图12 导航启动工具流程图
5.3 项目结构及实现
Electron 使用的是 Electron React Boilerplate 脚手架,使用 webpack 构建 UI 部分;Tauri 是使用官方的脚手架工具 - create-tauri-app,内置了 Vite,在前端框架上选了 React。
图13 Electron 与 Tauri 项目结构图
这个导航启动器主要涉及的功能点有:
1. 整个应用不展示关闭,最小化,最大化的按钮及整个菜单栏(menuBar),无边框窗口;
2. 视觉上整个应用是一个输入框,应用窗口的高度是根据网页内容的高度自适应;
3. 注册全局快捷键,显示应用,隐藏应用;
4. 监听按键,并使用默认浏览器打开链接。
5.3.1 功能点一
Electron 通过对主窗口初始化时修改配置,frame 设置成 false 可实现无边框窗口。
图14 Election 配置
在 Tauri 中,实现无边框窗口有 3 种方式:通过 tauri.conf.json 配置,通过 Tauri 提供的 JS API - @tauri-apps/api,通过 Rust 原生修改 window;这里我们选用在 tauri.config.json 中配置。
图15 Tauri 配置
5.3.2 功能点二
其输入框部分均由 React 实现,主要的差异在窗体根据内容高度动态调整窗体的高度,根据 document.body.offsetHeight 的高度设置 mainWindow 的高度;在 Electron 中,可以在渲染进程中发 IPC 通知主进程去修改,主进程监听到消息后进行高度修改。
图16 Electron IPC 通信修改
在 Tauri 中,相对比较方便,对于常用的功能都封装了 JS API,也就是前面提到的 @tauri-apps/api,直接导入方法调用即可。
图17 Tauri 在渲染进程修改
5.3.3 功能点三
注册全局快捷键,控制 mainWindow 的显示和隐藏。在 Electron 中,首先定义 registerGlobalShortcut 方法,在 app 启动后注册快捷键,主要是在主进程中操作。
图18 Election 注册快捷键
在 Tauri 中,得益于 JS API 的便利性,在渲染进程中就可以注册,因此只需要在 React 生命周期中执行注册。
图19 Tauri 注册快捷键
5.3.4 功能点四
为了方便演示,将直接对 document.body 进行 onkeydown 监听,上下光标选择对应的选项,回车或点击使用默认浏览器打开对应的链接,这里两者的实现很相似。
图20 Electron 与 Tauri 打开 URL
至此,主要功能已经完成,下一步将进入打包多平台的应用方式。
5.4 应用打包
Electron 中比较常见的有两种打包,electron-packager 和 electron-builder,electron-builder 的生态更好,这里选择 electron-builder。
图21 Electron 打包
Tauri 中则是内置在 cli 的打包方案,执行 yarn tauri build 即可。
表1 同一应用对比,相同 React 版本,未使用 UI 框架
图22 内存占用对比图
图23 Tauri 官方对比图
5.4 应用更新
1. NW.js 的时代已经过去,考虑 NW.js 的可以优先 Electron。
2. Tauri 表现不错,前景较好。它解决了 Electron 现有的很多问题,带来了简单便捷的开发体验,也期待 Tauri 的 roadmap 中集成 Deno 作为应用的后端处理,这样就可以继续使用 JavaScript/TypeScrupt 来实现应用后端逻辑,新项目可以考虑使用,但是还有一些问题需要改进以及 Rust 的学习曲线曲折,有一定的学习成本。
求分享
求点赞
求在看