GUI 跨平台解决方案-2017

July 18, 2017

这几天我想山寨一个 Notational Velocity。它的代码开源了,但年久失修。里面的 Objective-C 代码非常难读。Notational Velocity 我用起来非常舒服,我想让它能更通用一些,而不仅仅封闭在 Mac 平台。于是就打起了山寨它的主意。于是我便开始着手调研现在的跨平台 GUI 方案。

GUI 是客户端软件最基础,最必需的组件。现在 web 几乎统一了跨平台 GUI 解决方案。但如果你需要做与操作系统打交道的软件,你仍然无可避免的要去解决 GUI 跨平台的问题。 GUI 是阻止客户端软件跨平台最大的问题之一。这也是为什么现在为什么这么多软件全部 web 化的原因之一。

选择 web 做为 GUI 跨平台有如下好处: 1.用户不用安装软件,需要的时候拿来就用,用完即走。 2.开发者不用关心如何与不同操作系统交互的问题。 3.web 前端的学习成本非常低,适用性广。 4.web 现在的标准已经非常统一,很少会出现不兼容的情况。

web 本身是一种信息的组织方式,它通过 HTTP 协议把全世界的信息组织了起来。经过这么多年的发展它很偶然的渐渐变成了一种与用户直接交互的跨平台解决方案。web 这种典型的 C/S 架构是最适合 GUI 的,如果一个程序的 UI 不与其他部分分离,这个程序很快就会变得臃肿不堪(Linux xWindow 当年这么做还是有一定的道理)。只可惜这么多年了,操作系统的 UI 层一直未出现一个像 Chrome 这样统一的封装。但 web 另一大利器就是网络化,任何一个 web 应用不需要去做任何下载就能在浏览器里面使用,http 它统一网络 web 传输的协议。而平常操作系统程序都是基于本地的,即使有能做到像浏览器那样统一的封装,也无法做到像 web 这样遍布世界各地。互联网本质上就是信息的传播,而不是代码的传播,而 web 解决的问题刚好就符合了这一点。从这一点上来说,本地 GUI 程序和 Web 就不是一个层面的产物。浏览器相当于做出了一层统一的 GUI 接口的包装,有点类似于直接虚拟出了一层操作系统。web 就相当于运行在浏览器这一操作系统之内。由于安全性的问题,基于网络的 web 无法直接与操作系统打交道。

javascript 语言真的做到了像 java 那样 wite once, run everywhere. 不一样的地方在于虚拟机抽象层变成了浏览器。所以现在就诞生了 Electron 这种在客户端程序里面加入 webkit 做为引擎渲染存在本地 web 页面。使用这种解决方案典型的项目有 Atom, Github Desktop, Slack, visualstudio. 这些都是大公司的做出来的成果,目前看起来 Electron 是解决 GUI 跨平台很流行的方案,这得益于于 V8 的高性能,也和 nodejs 社区发展得如火如荼离不开关系。nodejs 几乎快变成了适用于任意地方万金油一样的编程语言。可是在客户端潜入一个 webkit 无论如何都会非常笨重,也难以做到轻量。你可能写一个简单的 hello world 弹窗最后打包的程序都需要 100M。 Electron 不适合轻量级的程序,也不是一个长期的 GUI 解决方案。它只适用于你只想做出一个凑合可用的跨平台应用。 Electron 应用更像是简单的把 HTML/CSS/JS 搬到一个不需要输入网址的浏览器里面。

对于以 web 构建 UI 的方式还有另外一个选择,就是 React Native。与 Electron 不一样的地方是 React Native 直接调用的是操作系统原生的 UI 组件,React Native 做了一层抽象使得写所有平台的 UI 就像在写 web 一样。它很轻量,不再需要一个浏览器里跑 APP 这种不环保的方式。更难得的是 React Native 为不同的屏幕做了适应,以适合现在复杂的手机屏幕尺寸。并且现在已经发展出了 React Native Desktop 了。以目前的趋势来看,不久以后 UI 会被 React Native 统一。因为它核心解决了一家公司开发移动 APP 成本过高的问题,也顺带解决了 UI 夸平台的痛苦。

另一个解决方案就是适用 Qt,Qt 在不同的平台上面把 GUI 直接画出来。Qt 发展了这么多年,一直处于非常冷清的状态,也很少见着一些大型的客户端是用 Qt 做出来的。Qt 也非常庞大,并不比基于 webkit 的 Electron 轻量。C++ 的使用和学习成本都非常高,做界面一致性远远也不如 web。除非不得已,现在几乎不会用 Qt 做为跨平台 GUI 的解决方案。C++ 自身跨平台代价也非常高。

我这几天仔细看了 libui 这个项目。本来考虑使用它来做为 GUI 实现,但随着我使用的过程中深入理解,才发现 libui 真的只是一个试验品,拿来做点可用的东西非常难。libui 它的实现方式就是使用 C 语言设计一层很薄的 API,底层调用的是不同平台的 native GUI。用 C 语言实现的好处是可以 binding 到不同的语言中去。libui 的问题在于它只针对一些基础的控件做了包装。虽然调用了操作系统原生的控件,但如果要使用一些稍微复杂一点的控件它却无法实现。由于不同的操作系统 GUI 交互式不一样的,它也无法很好在这方面做兼容去实现一些稍微复杂的交互。

综上所述,GUI 跨平台这个问题现在仍然没有太好的方案。如果要用户体验好,必需要考虑把代码的 GUI 相关部分单独剥离出来,然后为不同的平台单独写一份 GUI 相关的代码(也就是手动再做一遍像 libui 这样的封装)。就像 Photoshop 这种大型软件在 Windows 和 Mac 上做不同的实现一样。