vertxui_VertxUI:Java作为一种前端语言

vertxui

昨天,我接受了一份高级Java后端工作的面试。 他们问我是否是全栈开发人员。 尽管我的回答很自豪,但是我说我并不真的喜欢JavaScript,但是可以通过HTML / CSS和JavaScript代码找到自己的方式。 然后,我不得不解释为什么我不喜欢JavaScript。

我说我不喜欢是因为很多原因。 JavaScript在所有浏览器上均不能(或实际上:不能)相同地工作,存在一个包含所有变量的全局作用域,此外,还有成千上万个修复缺少的语言和API功能的库。 更糟糕的是,似乎没有一个可以被视为“默认”。 因此,没有机会知道您是否选择了持久的库。 我想我没有得到这份工作。

与大多数高级Java开发人员一样,我在2000年之前就编写了我的第一批JavaScript代码行。当然,这就是我反对它的原因。 但是情况已经改变。 如今,在经历了十多年的浏览器灾难之后,ECMAscript5已成为标准: 使用的所有浏览器中有97%以上都支持它 。 是的,JavaScript仍然具有丑陋的语言功能,但是您可以编写一段可在任何地方使用JavaScript代码。 而且JavaScript正在成为一种体面的语言,其中包括类,异常,lambda表示法,工作线程,包装,批注,期货,库管理等等。

但是,为什么要花时间学习新的语言功能和许多框架,冒着您学习永远不会更新的工具的风险呢? 除了直接在浏览器中外,还有另一种语言可以在任何地方运行。 如果Java可以直接在浏览器中运行怎么办?

请注意,许多JavaScript较新的功能已在多年前添加到Java中。 在几乎每种情况下,这意味着它们多年来已经相当稳定和成熟:

VertxUI和Fluent

VertxUI是一个100%Java库,它试图回答如果我们可以直接在浏览器中运行Java的情况。 而且由于纯CSS足以用于构建Web页面,因此VertxUI除了提供对浏览器的低级访问之外,还提供了一种轻量级的Fluent API(称为Fluent),该API仅使用代码即可简化HTML和CSS的构建。 Fluent使用与HTML标记对应的方法名称。 这些方法的第一个参数始终是CSS类。 因此,VertxUI没有任何模板,只有代码。 考虑引导程序中的菜单。 使用HTML,我们将定义一个Bootstrap菜单,如下所示:

<ul class="nav navbar-hav">
  <li class="active"><a href="#home">Home</a>
  <li><a href="#about">About</a>
</ul>


Using Fluent, we can use to generate the same HTML code like so:

...
Fluent ul = body.ul("nav navbar-nav");
ul.li("active").a(null, "Home", "#home", controller::onMenuHome);
ul.li(null).a(null, "About", "#about", controller::onMenuAbout);

变量主体是Fluent类的静态导入。 同样,您也可以使用方法控制台,文档和窗口。

实际上,Java源代码生成的代码比上面HTML代码片段做得更多。 它还显示了单击菜单项时如何调用Java代码。 在我们的示例中,控制器的实例( controller::someMethod )负责事件处理。

尽管没有在上一片段中显示,但还有一个类Store ,维护着Model的列表。 传统的MVC(模型视图控制器)设置不是必需的,但在编写JUnit测试时它很有用。

当然,您也可以使用lambda表示法。 例如,让我们创建一个Bootstrap表单。 .css().att()方法还用于向您展示它们的工作方式。 基本上,这是编写HTML和CSS所需的全部:

所需HTML代码段:

<div class="form-group">
  <label style="font-size: 200%" for="n">Wat</label>
  <input class="cssClass" type="text" id="n">
</div>

使用Fluent生成一些代码:

Fluent div = body.div("form-group");
div.label(null, "Wat")
   .style(Style.fontSize, "200%")
   .att(Att.for, "n");
div.input("cssClass", "text").id("n").
    keydown( (fluent, keyEvent) -> {
      console.log("You typed: " + fluent.domValue());
    });

怎么做

当然,Java代码不会在浏览器中运行。 之前已编译为JavaScript。 此时,人们在听到使用哪个编译器时常常会产生错误的印象,因此首先要提到的是VertXUI是使用TeaVM开发的。 但是,TeaVM并非没有缺陷。 特别是,有几个关于Lambda的错误。 因此,现在VertxUI使用GWT的交叉编译器来执行此操作,但不使用原始的GWT工具包。 不要将其与Vaadin或GWT本身混淆。 VertxUI是一种完全不同的独特方法。

在…模型上查看

当您将要更改DOM时,Fluent的真正功能就可以显示出来-因为您不必这样做。 与ReactJS(来自Facebook)相比,更改被尽可能高效地更新,这确保了当一个在线状态改变时,您的Facebook好友列表不会被完全重新呈现。

您可以使用.add()方法创建ViewOn<Model> 。 这需要两个参数:初始模型(或对模型的引用)和将模型转换为Fluent对象的方法。 例如:

结果

<table>
  <tbody class="striped">
  (per subscribed person:)
  <tr>
    <td class="fat">*name*</td>
    <td>*quantity*</td>
  </tr>
  </tbody>
</table>

码:

public View {
  private ViewOn<List<Person>> table;
 
  public void start(Controller controller){
    List<Person> initialPersons = controller.getPersons();
 
    Fluent middle = body.div();
    ...
    table=middle.add(initialPersons, persons -> {
      if(persons == null || persons.isEmpty()) {
        return Span("big","No people yet");
      }
      Fluent result = Table().tbody("striped");
      for(Person person:persons) {
        if(person.isSubscribed()) {
          result.tr(
              Td("fat",person.getName()),
              Td(null,person.getQuantity()));
        }
      }
      return result;
    });
  }
 
  public void syncPersons() {
    table.sync();
  }
}

您可能注意到了syncPerson()方法。 这将重ViewOn所有具有指向Person实体链接的ViewOn对象。 如前所述,您不需要控制器,但是这里控制器在更改后调用该方法。 请注意,编写非常复杂的用户界面(如向导)非常容易,因为您只是在声明性地写下UI的外观。 您甚至可以嵌套ViewOn对象。

所有代码都是纯Java,因此,如果您喜欢流,那就没问题了。 像其他许多容器(如标签)一样, tbody包含CSS类和Fluent对象的列表或流。

table = middle.add(initialPersons, persons -> {
  if (persons == null || persons.isEmpty()) {
    return Span("big", "No people yet");
  }
  return Table().tbody("striped", persons
      .filter(person -> person.subscribed())
      .map(person -> Tr(
          Td("fat", person.getName()), 
          Td(null, person.getQuantity()))));
});

查看…状态

在前面的Bootstrap菜单示例中,具有“活动”类CSS项在您单击时应切换到选定的项。 这就是我们所说的“状态”。 方便地识别状态并将其视为一个实体,而该实体永远不会保存到数据库中。 您还可以将ViewOn <>用于状态:

String initState = "home"; // or something else which you have extracted from the URL
...
Fluent menu = body.add(initState, state -> {
  Fluent ul = Ul("nav navbar-nav");
  ul.li(state.equals("home") ? "active" : null).a(null, "Home", "#home", controller::onMenuHome);
  ul.li(state.equals("about") ? "active" : null).a(null, "About", "#about", controller::onMenuAbout);
});

JUnit –单元测试

由于Fluent内部具有虚拟DOM,因此您无需启动浏览器就可以轻松地“滥用”此内容以进行JUnit测试。 这是非常快的,因为没有对JavaScript的编译,并且在后台也没有启动和停止浏览器。

在实践中,通常在JUnit测试中模拟类Store以防止网络流量,但是在下一个示例中,我们直接模拟Controller调用:

class Test {
 
  @Test
  public void test() {
    // Fake result after some network traffic
    List<Person> persons = new ArrayList<>();
    String name = "John " + Math.random();
    persons.add(new Person(name, 2000, true));
   
    // Startup sequence
    View view = new View();
    Controller controller = Mockito.spy(Controller.class);
    Mockito.when(controller.getPersons()).thenReturn(persons);
    view.start(controller);
   
    assertEquals("1 row",1,VirtualDomSearch.getElementsByTagName("TR", body));
    List<Fluent> tds = VirtualDomSearch.getElementsByTagName("TD", body);
    assertTrue("2 columns", 2, tds.length());
    assertTrue("name test", name, tds.get(0).txt());
  }
}

JUnit –集成测试
随着项目的发展并开始使用外部JavaScript库,集成测试变得越来越重要。 在Fluent中,您可以在具有“注册并运行”构造的无头浏览器中执行双语言测试。 与Selenium相比,您对运行时环境的控制要多一些,因为您可以轻松地运行JavaScript资产和Java资产,并将它们组合在一起进行一次测试。

作为示例,我们以第一个沉闷的Bootstrap菜单示例为例,并通过直接调用controller.onMenuAbout()模拟菜单单击。 让我们看看更改“ active”类内容的前面的示例是否真正起作用。 以下代码确实是您所需要的。 Java到JavaScript的编译是即时进行的:

class Test extends TestDOM {
 
  @Test
  @GwtIncompatible
  public void test() throws Exception {
    System.out.println("Java");
    runJS(100); // run slot '100'
  }
 
  @Override
  public Map<Integer, Runnable> registerJS() {
    Map<Integer, Runnable> result = new HashMap<>();
    result.put(100, () -> testWithDOM()); // register slot '100'
    return result;
  }
 
  public void testWithDOM() {
    console.log("JavaScript");
     
    View view = new View();
    Controller controller = new Controller(new StoreEmpty(), view);
    view.start(controller);
 
    // search the active menu item
    NodeList actives = document.getElementsByClassName("active");
    assertEquals("quantity test 1", actives.length(), 1);
    assertTrue("titletest 1",((Element) actives.item(0).getChildNodes().at(0))
      .getTextContent().equals("Home"));
     
    controller.onMenuAbout(null, null);
     
    // search again
    actives = document.getElementsByClassName("active");
    assertEquals("quantity 2", actives.length(), 1);
    assertTrue("titletest 2",((Element) actives.item(0).getChildNodes().at(0))
      .getTextContent().equals("About"));
  }
}

请注意,本示例可以放在非DOM测试中,该测试运行速度快得多。

VertX

您可以在任何后端软件中运行VertxUI,但与VertX一起提供了多种功能,例如FigWheely和POJO流量。 使用VertX的VertxUI容易比容易:只需启动main()并将浏览器指向http:// localhost即可 。 您无需安装任何IDE插件。 您也不必处理* .war或* .jar。 刚开始上主课,您就很好了!

VertX是完全异步的,可与回调一起使用,就像JavaScript一样。 因此,例如,它直到TCP数据到达时才阻塞,而是在到达某些数据时继续运行堆栈。 最大的区别是,由于Java是一种结构化的语言,因此您永远不会像JavaScript那样遇到回调地狱。 当异步发生时,您可能会在另一个类中调用另一个方法。

VertX – FigWheely

FigWheely是VertxUI的运行时助手。 它使WebSocket在浏览器中保持打开状态,并在服务器上的文件更改时接收通知。 如果已更改的文件恰好是.java文件,FigWheely将重新编译您的浏览器代码并通知浏览器。

FigWheely的工作原理与VertxUI一样,没有任何IDE插件,因为对JavaScript的编译是在启动(VertX)服务器并找到源代码时发生的。 在启动过程中,还会生成一行index.html,但是您也可以关闭它以使用现有网站。 或者,您可以在使用jQuery Mobile时将HTML本身用作HTML模板。

VertX – POJO

VertxUI促进了服务器和浏览器之间的POJO通信,以进行Ajax调用,WebSocket,sockJS和VertX事件总线。 即使在下面使用JSON,这也意味着强类型的流量。 使客户端和服务器使用相同的语言具有许多不错的优点:当您要添加表列时,可能只是在实体中添加一行代码,在视图中添加一行代码以获取额外的“ TD”。

这是带有POJO接收器的聊天应用程序的客户端示例:

WebSocket socket = window.newWebSocket("ws://localhost/chatWebsocket");
socket.setOnmessage(event -> {
  // POJO: receive the a color and put it between a new <li>...</li>
  if (POJOfy.socketReceive(urlPOJO, event, POJOMapper,
    pojo -> messages.li("colored", "Received: " + pojo.getColor()))) {
    return;
  }
  // otherwise, receive the text and put it in a new <li>..</li>
  messages.li("flat", ((MessageEvent) event).getData().toString());
});

这是服务器端,它也可以接收POJO。 它看起来是如此简单,以至您可能会忘记这是一个功能非常强大的Web服务器,它比常规的锁定多线程servlet环境更强大:

List<String> ids = new ArrayList<>(); // all ids
vertx.createHttpServer().websocketHandler(socket -> { // entering
  String id = socket.textHandlerID();
  ids.add(id);
  socket.closeHandler(data -> { // leaving
    ids.remove(id);
  });
  socket.handler(buffer -> { // receiving
    if (POJOfy.socket(socket, View.urlPOJO,buffer, Dto.class,aService::handle)){
      return;
    }
    String message = buffer.toString();
    ids.forEach(id -> vertx.eventBus().send(id, message)); // broadcasting
  });
}).listen(80);

结语

无论将来使用客户端和JavaScript语言发生什么事情,您都可以使用成熟的Java语言(由成熟的VertX服务器环境支持并由任何成熟的支持)创建非常可测试的单页Web应用程序。 CSS框架,如Bootstrap。 仅VertxUI的测试设施就应该足够有趣,可以为新项目试用VertUI。 更不用说POJO流量,强类型代码,完善的IDE等,换句话说:Java。

JavaScript及其库将不断发展,但这将需要数年时间,而且机会很小,您现在就可以选择合适的库,并防止重构和学习在习惯之前不赞成使用的新语言功能。

如果您熟悉React.js或Angular,则习惯于仅更改模型以更新视图。 实际上,视图和模型之间的这种无缝集成使React和Angular变得流行。 这是对旧方法进行手动修改DOM的重大改进。 更改DOM容易出错。 使用框架可以使您编写简洁的代码。 VertxUI将此想法带入了Java世界。

本文最初在Beyond Java上发表。

翻译自: https://jaxenter.com/vertxui-java-front-end-language-135331.html

vertxui

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值