从一开始,Vaadin 就是 CUBA 平台 UI 的基石和重要组成部分。由于其独具创造性的 UI技术,它帮助 CUBA 将企业级应用的 UI 开发带到了市场广阔的 WEB 领域 。Vaadin 最令人兴奋的特性之一是整个开发都是同构 的,并且主要使用Java完成,这样避免了开发人员直接面向变化多端且发展迅速的前端世界。
如您所见,Vaadin 重要部分之一是功能丰富的 UI(在Vaadin 8 中是基于GWT小部件)。
与现代 Web UI 套件相比,即使在今天,Vaadin 部件仍是最复杂最先进的,尤其是在企业需求方面。 这里我们主要说的是那些在典型 CUBA 应用程序中重度使用组件,比如表格、网格、下拉框等。其它流行的 UI 套件中,是很难找到提供拖拽列排序或者动态列控制的表格的。
CUBA 与 Vaadin 的故事
CUBA 平台使用 Vaadin 有着悠久的历史。CUBA 平台为用户提供了从 Vaadin 5 到 Vaadin 8 的无缝迁移。能支持这种迁移,是因为我们在 Vaadin 之上构建一个抽象API层。此外,我们扩展了一些组件,甚至直接 Fork 了框架本身来为我们的客户提供独特的功能、注入所需的扩展点。在服务端,平台提供数据绑定和数据感知组件,这是 CUBA 框架中通用 UI 的最重要功能。
为了提高界面开发效率并启用快速开发工具(Studio),我们引入了XML描述 — 允许以声明式的方法定义支持数据绑定的UI:
CUBA 平台中以使用 XML 定义界面:
<layout>
<grid spacing="true" height="200">
<columns count="4"/>
<rows>
<row flex="1">
<label value="Field 1" align="MIDDLE_LEFT"/>
<textField height="100%"/>
<label value="Field 2" align="MIDDLE_LEFT"/>
<textField height="100%"/>
</row>
<row flex="2">
<label value="Field 3" align="MIDDLE_LEFT"/>
<textField height="100%"/>
</row>
</rows>
</grid>
</layout>
纯 Vaadin 定义界面:
GridLayout tableHeader = new GridLayout(3, 2);
Label nameLabel = new Label("Field 1");
nameLabel.setWidth(lastnameWidth + 2 * 6, Unit.PIXELS);
tableHeader.addComponent(nameLabel, 0, 0, 0, 1);
Label countryLabel = new Label("Field 2");
countryLabel.setWidth(countryWidth + 2 * 6, Unit.PIXELS);
tableHeader.addComponent(new Label("Field 3"), 1, 0);
Label bornLabel = new Label("Field 4");
bornLabel.setWidth(bornWidth + 2 * 6, Unit.PIXELS);
tableHeader.addComponent(bornLabel, 2, 0, 2, 1);
tableHeader.addComponent(countryFilterBox, 1, 1);
// Put the header and table inside a vertical layout
layout.addComponent(tableHeader);
layout.addComponent(table);
// Adjust the table height a bit
table.setPageLength(table.size());
我们设法构建了自己的一套组件(基于 Vaadin 元素):
- GroupTable — 分组表格
- Form (以前的 FieldGroup) — 表单
- PickerField — 选择器控件
- LookupPickerField — 选择器控件
- TokenList — 下拉选择器
- MaskedField — 掩码字段
- SuggestionField — 建议字段
- CurrencyField — 货币组件
也就是说,CUBA 在 Vaadin 框架之上提供了很多功能,以使开发人员更轻松,同时在更高的技术层次上进行开发。CUBA 团队做了非常多的工作,使 CUBA 框架中 Vaadin 框架的升级过程更加平滑。
新的挑战
定制和本地交互
GWT 非常复杂,创建 UI 部件是一个具有挑战性且耗时的过程。经验丰富的开发人员肯定会知道,要突破某个对原生平台的抽象层进行开发时,一般会代价高昂。对于GWT,我们必须在 Java 中与浏览器 JS API 进行交互。
响应式布局
对于后台系统的界面,也需要可以针对不同的屏幕尺寸进行调整,这已经是一个关键的技术指标。正是由于上述在原生平台上添加的额外抽象,使得响应式 UI 很难实现。尽管您可以使用 CssLayout 或一些扩展来创建响应式 UI,但是基于服务端的布局和计算在这种情况下不能很好地发挥作用。
使用第三方库
Web 的发展非常迅速,有大量的Web 库(npm> 1M)在 Vaadin 8 应用程序中几乎用不上,因为它没有使用现代的前端工具和构建系统。
GWT 开发陷入困境
在某个时候,Google 停止了对 GWT 的支持。不仅是官方支持的停止,整个生态系统都在萎缩。
Vaadin Flow
为了对前端生态系统更加开放,Vaadin 开始开发 Vaadin 框架的后继产品。新技术的核心是 Vaadin Flow,该技术为基于 Web 组件的新 UI 层提供服务端模型和基本数据绑定。
考虑下图:
如您所见,Vaadin 已将基于 GWT 的客户端替换为基于原生 Web 技术的新客户端。
Vaadin 组件
新的 Vaadin 组件是 Vaadin GWT 部件的后继产品。它们是基于纯 Web 技术(HTML、JavaScript)和 Polymer2 重新构建的 Web 组件。
Web组件 (Web Component)
最初,Web 组件是大多数现代浏览器中实现的标准技术的一个集合:
- Custom Elements — 自定义元素
- Shadow Dom
- HTML Templates — HTML 模板
- HTML Imports -> ES Modules
长期以来,Web 组件很有前途,许多人(包括我在内)都将它们视为 React 和 Angular等框架的原生替代品,这些框架也利用基于组件的方法。但是随着时间的流逝,这些标准中的一些已被浏览器丢弃,而另一些则需要进行重大改进,这一点已经显而易见。如今,在上面的列表中,只有自定义元素和 Shadow DOM 仍用于 Web 应用程序开发中。Chrome 甚至删除了 HTML 导入。如今的 HTML 模板似乎也已经过时了,比如,新的Polymer 技术 :lit-html ,都不是在界面直接使用 html 模板。
我们也在尝试使用 Web 组件,这是我们对基于 Polymer 库构建C端UI所做尝试的一部分。后来,我们决定将工作重点转向基于 React 的方案,尽管 Polymer 负有振兴 Web Component 技术的使命,但开发人员的体验却很差、生态系统很小(即使已经存在了几年),最后还含含糊糊地发布了 Polymer 3 ,并且官方不推荐将 Polymer 3 用于新的项目。Polymer 用户不得不等了将近一年,直到 lit-html 和 LitElement 最终发布。
根据经验,我们另一条结论是:尽管有大佬们极力倡导 "Use The Platform",但是在开发现代前端应用程序时,仍然不能摆脱转译/打包步骤。标准试图通过协调所有浏览器厂商这种难度比较大的方式来统一API,而社区则通过创建许多工具和库来解决相同的问题。
译者注:"Use The Platform" 口号的含义是 WEB 前端开发时放弃使用第三方库,都使用浏览器提供的技术。这一口号出现的背景是现代浏览器已经实现了许多之前由第三方提供的功能。详细资料可参考这篇文章:Why You May Not Need A Framework
例如,Shadow DOM的主要目的是封装CSS样式,以免溢出到组件的本地 DOM 或从组件的本地 DOM 溢出。这个主意很棒,但是大多数浏览器都花了几年的时间(幸好Edge移到了Chromium)。同时,React 生态系统由大量样式库实现,这些样式库可以达到同样的目标,甚至可以解决更多问题,而不会产生Shadow DOM的重大缺陷。
但是,Web 组件具有非常重要的特性:它们是平台(浏览器)的一部分。从理论上讲,它们不受任何特定框架的约束,是通用的,可以在任何地方使用。从这个角度来看,这似乎是UI Kit或独立组件(而不是应用程序)的合理选择,不仅 Vaadin 这样做,Ionic和SAP 也这样做。
Vaadin 14
基于 Vaadin Flow 的 Vaadin 10 已于2018 年中期发布。很明显,该 UI 套件少了许多重要组件,仅包含了基本组件。此外,客户端构建管道还包括一个依赖管理器:Bower - 该工具已于2017年弃用,并且不能与事实上的标准- NPM 生态系统交互。
因此,我们将 Vaadin 10 的发布视为实验性的,并决定继续等待,直到新技术变得更稳定。在 Vaadin 14 LTS 于 2019年8月发布之前,共经历了3个主要版本。而 Vaadin 14 包含了对 NPM 的高度支持和更强大的 UI 套件。这促使我们仔细审视并亲身体验 Vaadin 14 。
UI套件
虽然没有深入研究代码库,但是很明显可以看到,与 Vaadin 8 部件相比,许多属性和行为发生了变化。通常,这不是一件不能接受的事情,但是对于CUBA,这意味着在某些方面,当前已经支持的功能或 API 将没有直接的替代品。
在完整性方面,仍然缺少一些已在 CUBA中使用的核心组件:
- Calendar — 日历
- Tree — 树
- TwinColumn — 双列
某些以前免费的组件和功能成为了专业(收费)组件的一部分:例如,RichTextArea 现在是专业组件的一部分、Grid 的编辑模式只在Vaadin Grid Pro 中提供 。
PickerField
作为评估过程的一部分,我们使用 Vaadin 14 重新实现了 CUBA 的 PickerField 组件:
说到服务端,Vaadin Flow 提供了惊人的功能,可以使用 Java API 与客户端(DOM元素、事件等)进行交互。Vaadin 组件附带了方便的 Java API:
Accordion accordion = new Accordion();
...
accordion.open(1);
非 Vaadin 组件没有此类 API,但是您仍然可以通过DOM API对任何元素使用通用方法:
例子1
if (value == null) {
getElement().removeProperty("value");
} else {
getElement().setProperty("value", getStringRepresentation(value));
}
例子2
getElement().appendChild(new Element[]{Element.createText(text)});
尽管服务端非常漂亮和简洁,但客户端却花了我们 90% 的精力。我们首先要提到的是,核心 Vaadin 组件当前是由 Polymer 2 构建的。为了支持 Vaadin 14+ 的 Polymer 3,它们似乎已经进行了自动转换。通常,Polymer 2 和Polymer 3具有相同的API(这就是为什么可以进行自动转换的原因),但是,导入和样式声明之间存在细微的差异。
另一个棘手的事情是样式和定制:由于使用了 Shadow DOM,你根本无法将样式应用于任意元素(仅可用于设计为可通过使用自定义CSS属性进行样式设置的元素)。Vaadin 组件具有用于自定义的扩展点( Shadow DOM 的另一个强大但复杂的部分)。它们非常适合简单的用例,但是在尝试实现更高级的用例时,您很快就会遇到限制。
因此,对 PickerField 的实现,最终是通过复制粘贴样式和 Vaadin 组件的其他部分来完成的。并且是在原生 input 元素之上构建该组件(唯一可从 @vaadin 导入的可重用的东西是几个Mixins)。
我们不会抱怨 Vaadin,因为它没有被设计(也不应该)作为另一个UI工具包的基础。 只能说明如果我们要将 CUBA 中已经提供的 UI 功能使用 Vaadin 14 来实现,那么我们将要做大量的工作。而这些工作都要基于 Polymer 3(已经进入维护模式) 来进行,Polymer 3 与Polymer 2 有一样差的开发者体验。
最新消息
就在撰写本文时,Vaadin 宣布所有核心组件都将使用 TypeScript 和 LitElement 上进行重写。我们对这个决定持肯定态度,因为我们在 TypeScript 方面的丰富经验证实,它可以避免 JS 中缺少静态类型所引起的大量错误、有助于理解代码库的结构、执行安全的重构等。
不过,LitElement/lit-html 看起来有点令人怀疑,尽管我们知道它是Polymer 的继承者,并且利用了React 的强大的声明式渲染(view=f(state) )方法。但是它仍然存在以下问题:
- 初出茅庐
- 需要运行时(与 Stencil 和 Svetle 之类的编译的方式不同)。为了同时支持基于 Polymer 和 Lit 的组件,Vaadin 会将两个库都下载到客户端
- IDE 支持差。有一些 VS Code 插件,但是没有支持 IntelliJ/WebStorm 的,这使得llit 模板看起来真的很乱。
- 对 SSR 不友好。
也出现了许多新问题:
LitElement + TypeScript 是否会取代当前基于Polymer 3的方法来开发应用程序前端?
如果是,那么类似 React 的渲染将如何与服务端 Java API一起工作?
更新资料
Vaadin 15 带来了客户端引导程序和TypeScript 支持。
结论
Vaadin是一种独特的产品,可为 Java 提供便利的 Web 开发支持。Vaadin Flow 带来了一种全新的客户端方法,我们认为这很有希望。但是,组件集仍在不断发展,并正在走向稳定。
我们可以肯定的是:由于Vaadin 使用了全新的客户端技术,我们将不能提供平滑过渡到基于 Vaadin Flow 的新 UI 的迁移路径。
同样,我们认为现在就开始在客户端技术上对所有 CUBA 组件进行大规模迁移仍然有许多不确定因素。我们决定将迁移工作推迟到新的 Web 组件集可用之后。我们仍将密切关注 Vaadin 的发展,并在其变得更加稳定后对其进行重新评估。
同时,我们还尝试提供一种可选的、客户端友好的方法来创建 UI:请参阅我们最近关于TypeScript SDK和 React Client 的博客文章。