HTTP 与 Web

使用 React 和 Vert.x 开发单页面应用

本文档将向您展示如何使用 React 和 Vert.x 开发单页面应用 (SPA)。

您将构建什么

您将创建一个 React 前端,通过 HTTP 与 Vert.x 后端进行通信。

开发工作流

在开发 SPA 时,更新 Javascript、HTML 或 CSS 文件后能够立即获得反馈非常方便。使用 React,这需要您启动一个 npm 开发服务器,该服务器用于处理前端资源的请求

Frontend resource

但是那些必须由 Vert.x 处理的请求呢?您将配置项目,使前端开发服务器将 API 请求代理到后端

API request

您需要什么

  • 文本编辑器或 IDE

  • Java 11 或更高版本

  • Maven 或 Gradle

  • Node

  • npm

  • npx,一个 NPM 包运行器

创建项目

此项目的代码包含功能等效的 Maven 和 Gradle 构建文件。

使用 Maven

在您的 Maven POM 文件中添加 vertx-web 依赖

Maven pom.xml
  <dependencies>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-web</artifactId>
      <version>${vertx.version}</version>
    </dependency>
  </dependencies>

然后添加 exec-maven-plugin

Maven pom.xml
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.5.0</version>
        <configuration>
          <mainClass>io.vertx.howtos.react.BackendVerticle</mainClass>
        </configuration>
      </plugin>

使用 Gradle

假设您使用 Kotlin DSL 的 Gradle,添加 vertx-web 依赖

Gradle build.gradle.kts
dependencies {
  val vertxVersion = "5.0.0.CR2"
  implementation("io.vertx:vertx-web:${vertxVersion}")
}

然后配置应用程序主类

Gradle build.gradle.kts
application {
  mainClass = "io.vertx.howtos.react.BackendVerticle"
}

通过 HTTP 暴露消息服务

让我们从后端服务开始。它将通过返回一个问候消息来处理 /api/message 路径上的请求

Java src/main/java/io/vertx/howtos/react/BackendVerticle.java
    Router router = Router.router(vertx);
    Route messageRoute = router.get("/api/message"); (1)
    messageRoute.handler(rc -> {
      rc.response().end("Hello React from Vert.x!"); (2)
    });

    router.get().handler(StaticHandler.create()); (3)

    return vertx.createHttpServer()
      .requestHandler(router)
      .listen(8080);
1 定义了一个 Vert.x Web Route 来匹配 /api/message 路径上的 HTTP 请求
2 Route 处理器用问候消息回复请求
3 需要 StaticHandler 来处理静态资源的请求
您可能想知道,如果前端开发服务器处理静态资源,为什么我们还需要一个 StaticHandler?请记住,当整个应用程序构建并投入生产时,前端将与后端 HTTP 服务器捆绑在一起并由其提供服务。

在测试实现之前,BackendVerticle 需要一个 main 方法

Java src/main/java/io/vertx/howtos/react/BackendVerticle.java
  public static void main(String[] args) {
    Vertx vertx = Vertx.vertx(); (1)
    vertx.deployVerticle(new BackendVerticle()).await(); (2)
    System.out.println("Verticle started!");
  }
1 创建一个 Vertx 上下文
2 部署 BackendVerticle

您可以运行应用程序

  • 直接从您的 IDE 中,或

  • 使用 Maven:mvn compile exec:java,或者

  • 使用 Gradle: ./gradlew run (Linux, macOS) 或 gradlew run (Windows)。

以下示例使用 HTTPie 命令行 HTTP 客户端。如果您的系统尚未安装它,请参阅安装文档。

要接收问候语,请打开您的终端并执行此命令

http :8080/api/message

您应该看到

HTTP/1.1 200 OK
content-length: 24

Hello React from Vert.x!

在浏览器中显示消息

我们有了一个完全可用的后端,现在可以创建前端了。为此,请使用 npx 运行 create-react-app

cd src/main
npx create-react-app frontend

这将

  • 创建一个定义依赖项以及构建和运行脚本的 package.json 文件

  • 安装依赖项

  • 生成一个骨架应用程序

在本指南中,前端代码作为后端项目的一部分存在,位于 src 目录中。这更容易上手,特别是如果您的团队包含更多偏向后端的开发人员。但是,随着项目的增长,您可能更倾向于将前端和后端拆分为独立的模块。

这里对骨架应用程序不太感兴趣,所以我们将其删除

rm -rf frontend/src/*

然后打开您喜欢的编辑器并实现 React 前端

Javascript src/main/frontend/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

class Greeter extends React.Component { (1)

  constructor(props) {
    super(props);
    this.state = { (2)
      message: "Default message"
    }
  }

  componentDidMount() { (5)
    fetch("/api/message")
      .then(response => response.text())
      .then(text => this.setState({message: text}));
  }

  render() { (3)
    return (
      <div>
        <span>{this.state.message}</span>
      </div>
    );
  }
}

ReactDOM.render( (4)
  <Greeter/>,
  document.getElementById('root')
);
1 我们的前端由一个单独的 React Greeter 组件组成
2 Greeter 组件持有状态,即要在浏览器中显示的消息
3 消息显示在一个简单的 HTML span
4 Greeter 组件在网页中渲染
5 初始渲染后,会向后端发送一个 HTTP 请求;结果用于更新组件状态

最后但同样重要的是,您必须配置前端开发服务器以将 API 请求代理到后端。在您的编辑器中打开 package.json 文件并添加

Javascript src/main/frontend/package.json
"proxy": "https://:8080"

就是这样,打开一个终端并启动前端开发服务器

cd src/main/frontend
npm start

浏览器选项卡应该会自动打开并指向 https://:3000

您应该看到

Hello React from Vert.x!

整合所有

当然,在生产环境中您不会启动前端开发服务器。因此,Maven POM(或 Gradle 构建)文件应配置为

  • 运行前端构建

  • 将静态文件复制到 src/main/resources/webroot 文件夹

您还记得第一节中的 StaticHandler 吗?它默认在 webroot 文件夹中查找静态文件。这就是为什么您必须将静态文件复制到 src/main/resources/webroot 的原因。

将这些插件添加到您的 Maven POM 文件中

Maven pom.xml
      <plugin>
        <groupId>com.github.eirslett</groupId>
        <artifactId>frontend-maven-plugin</artifactId>
        <version>1.7.6</version>
        <configuration>
          <nodeVersion>v22.12.0</nodeVersion>
          <npmVersion>10.9.0</npmVersion>
          <workingDirectory>src/main/frontend</workingDirectory>
          <installDirectory>target</installDirectory>
        </configuration>
        <executions>
          <execution>
            <id>install-node-and-npm</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>install-node-and-npm</goal> (1)
            </goals>
          </execution>
          <execution>
            <phase>generate-resources</phase>
            <id>npm-install</id>
            <goals>
              <goal>npm</goal>
            </goals>
            <configuration>
              <arguments>install</arguments> (2)
            </configuration>
          </execution>
          <execution>
            <id>npm-run-build</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>npm</goal> (3)
            </goals>
            <configuration>
              <arguments>run build</arguments>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
        <executions>
          <execution>
            <id>copy-to-webroot</id>
            <phase>process-resources</phase>
            <goals>
              <goal>copy-resources</goal> (4)
            </goals>
            <configuration>
              <outputDirectory>${project.build.outputDirectory}/webroot</outputDirectory>
              <resources>
                <resource>
                  <directory>${basedir}/src/main/frontend/build</directory>
                  <filtering>false</filtering>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
1 在构建目录中下载 Node 和 npm 允许在 CI 上运行前端构建,即使它们可能不存在
2 使用 npm install 下载前端依赖项
3 创建前端的生产就绪构建
4 将静态文件复制到 src/main/resources/webroot

如果您使用 Gradle,首先添加 Gradle NPM 插件

Gradle build.gradle.kts
import com.github.gradle.node.npm.task.NpmTask

plugins {
  java
  application
  id("com.github.node-gradle.node") version "7.0.2"
}

然后配置类似于我们使用 Maven 所做的

Gradle build.gradle.kts
node {
  version = "22.12.0"
  npmVersion = "10.9.0"
  download = true
  nodeProjectDir = File("src/main/frontend")
}

val buildFrontend by tasks.creating(NpmTask::class) {
  args = listOf("run", "build")
  dependsOn("npm_install")
}

val copyToWebRoot by tasks.creating(Copy::class) {
  from("src/main/frontend/build")
  destinationDir = File("build/classes/java/main/webroot")
  dependsOn(buildFrontend)
}

val processResources by tasks.getting(ProcessResources::class) {
  dependsOn(copyToWebRoot)
}

确保所有之前的 npmmvngradlew 执行都已终止,然后启动 Vert.x 服务器

  • 使用 Maven:mvn compile exec:java,或者

  • 使用 Gradle: ./gradlew run (Linux, macOS) 或 gradlew run (Windows)。

浏览到 https://:8080,您应该会看到

Hello React from Vert.x!

总结

本文档涵盖了

  1. 使用 create-react-app 创建一个新的 React 应用程序

  2. 运行一个前端开发服务器(用于实时重载),它将 API 请求委托给 Vert.x 后端

  3. 在生产环境中将前端静态文件与 Vert.x 类捆绑在一起