HTTP 与 Web 开发

使用 React 与 Vert.x 构建单页应用 (SPA)

本指南将向你展示如何协同开发一个以前端 React 单页应用为界面、以 Vert.x 为高性能后端的现代 Web 架构体系。

你将构建什么

你将构建一个 React 前端应用,通过 HTTP 与后端的 Vert.x REST 服务进行通信。

开发期工作流 (Development Workflow)

在开发单页应用(SPA)时,对 JavaScript、HTML 或 CSS 文件的每次修改都能即时获得热重载(Fast Refresh)反馈至关重要。 在 React 生态中,这通常需要使用 npm 启动一个前端本地开发服务器来分发静态资源:

Frontend resource

那么,前端发起的业务 API 请求该如何无缝交由 Vert.x 后端处理呢? 我们将配置前端工程的代理机制,让前端开发服务器自动将 /api 请求反向代理给 Vert.x:

API request

前置要求

  • 文本编辑器或 IDE

  • Java 11 或更高版本

  • Maven 或 Gradle

  • Node.js 与 npm

  • npx(NPM 包执行工具)

创建工程项目

本项目代码同时支持 Maven 与 Gradle。

使用 Maven

在 Maven pom.xml 中引入 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

使用 Gradle Kotlin DSL 时,添加 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 定义一条匹配 /api/message 路径的 Vert.x Web 路由;
2 路由处理器向客户端响应一条欢迎文本;
3 挂载 StaticHandler,用于向浏览器分发前端生产编译后的静态文件。
你可能会好奇:既然开发期前端开发服务器已经分发了静态文件,为什么后端还要挂载 StaticHandler?请记住,在生产环境中,前端会被整体打包并嵌入后端 JAR 中,由 Vert.x 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。

服务启动后,使用 curl 或 httpie 进行测试:

http :8080/api/message

你将看到返回:

HTTP/1.1 200 OK
content-length: 24

Hello React from Vert.x!

在浏览器中显示响应消息

后端服务运行正常后,我们开始创建 React 前端应用。 在 src/main 目录下使用 npx 运行 create-react-app:

cd src/main
npx create-react-app frontend

这会自动生成基础的项目骨架与依赖配置。 清空默认样板代码:

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 将组件挂载到 DOM 树中;
5 组件完成初次挂载渲染后,通过 fetch 向后端 API 发起 HTTP 请求,并将返回的数据更新至组件状态中。

至关重要的一步:配置开发服务器代理。在 src/main/frontend/package.json 中添加:

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

现在,启动前端开发服务器:

cd src/main/frontend
npm start
Hello React from Vert.x!

生产环境打包集成

在生产环境中,我们不会运行独立的前端开发服务器。Maven 或 Gradle 会被配置为:

  • 自动执行前端构建打包;

  • 将生成的静态资源复制到 src/main/resources/webroot 目录下。

在 Maven pom.xml 中引入前端构建插件:

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.js 与 npm,确保在无 Node 环境的 CI 流水线上也能稳定构建;
2 执行 npm install 下载前端依赖;
3 运行 npm run build 生成高度压缩的生产制品;
4 将生产制品自动复制到 src/main/resources/webroot。

完成打包后,直接启动 Vert.x 服务器并在浏览器访问 http://localhost:8080,即可看到完整打包一体化的 SPA 单页应用。

本文总结

本指南涵盖了:

  1. 使用 create-react-app 快速搭建 React 前端;

  2. 利用开发期代理机制实现前端热重载与后端 API 联调解耦;

  3. 在生产构建流程中将前端静态产物与 Vert.x 后端合并打包分发。