用 Spring Boot 打包你的 React 应用

简介: 先讲一讲这篇文章的背景故事。之前我的团队需要在我们需求的基础架构上节省一些资金,并且由于我们要构建的这个应用程序中,大部分负载都会在客户端而非服务端上,所以我们决定试验一下能否将一个 Spring 应用程序与一个 React 应用结合起来,并打包成一个 war 文件。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

先讲一讲这篇文章的背景故事。之前我的团队需要在我们需求的基础架构上节省一些资金,并且由于我们要构建的这个应用程序中,大部分负载都会在客户端而非服务端上,所以我们决定试验一下能否将一个 Spring 应用程序与一个 React 应用结合起来,并打包成一个 war 文件。

这篇文章会告诉你如何将 Create React App 与 Spring Boot 结合使用,从而为你打包出单个 war 文件。

Spring Boot 和 Create React App 的基本功能介绍

  • Create React App 可帮助你非常快速地启动 React 项目。它为你提供了启动并尽快运行项目所需的全部基本功能。
  • Spring Boot 可以帮助你快速而轻松地启动和维护 Spring 应用程序。

步骤

1.目标

  • 在单一 war 文件中包含前端和后端,具有优化的生产构建
  • 保留 Create React App 所提供的好处,如热重载等

2.设置

附注:我选择的 IDE 是 IntelliJ。当使用 React 代码时,我通常会切换到 VS Code。你可以随意使用自己习惯的方法

  • 在 Github 上创建一个空的仓库,并添加自述文件、gitignore 和许可证等。
  • 转到( https://start.spring.io )创建你的 Spring 应用程序,并下载到本地。Group 和 Artifact 也可以随意设置。

FE68688D_9543_4726_B094_33FE89785E8D

GroupId:e.the.awesome
Artifact:spring-react-combo-app

3.将下载的项目解压缩到你的 git 目录中并提交。

你的 SpringReactComboAppApplication 看起来应该像这样。

package e.the.awesome.springreactcomboapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class SpringReactComboAppApplication  extends SpringBootServletInitializer{

    public static void main(String[] args) {
        SpringApplication.run(SpringReactComboAppApplication.class, args);
    }

}

4.现在创建一个基本服务。

我们将其称为 DadJokesController。这应该在与 SpringReactComboAppApplication 文件所在的文件夹中创建。我知道这不是正确的 Rest API 格式,但现在暂时忽略它。

package e.the.awesome.springreactcomboapp;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DadJokesController {
    @GetMapping("/api/dadjokes")
    public String dadJokes() {
        return "Justice is a dish best served cold, if it were served warm it would be just water.";
    }
}

5.在你的终端运行

07EDC202_94BD_4454_B318_A4A0DCC03493

然后在浏览器中检查 http://localhost:8080/api/dadjokes 。你应该会看到我们添加到控制器中的 dad joke。

6.要创建你的 React 应用,只需在根目录中运行

89514630_E58A_46e5_9E06_BFB12E207AC9

你可以随心所欲地调用它,我这里只调用我的 basic-frontend-app

7.要运行前端应用程序:

7DC44D17_15B6_4569_B384_19A6F2A2AAAE

8.解决代理问题

由于我们要将 Dad Jokes 服务集成到前端,因此首先我们要解决代理问题。你可能已经注意到了,你的服务从 localhost:8080 开始,而前端从 localhost:3000 开始。如果我们尝试从前端调用服务,具体取决于你的浏览器,你可能会收到一个 CORS 错误。

91ACE1DE_E478_4517_9444_52A7B6BF1127

解决此问题的最简单方法是让前端代理处理从端口 3000 到 8080 的所有请求。此更改将在 package.json 文件中进行:

{
  "name": "basic-frontend-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.3.1",
    "react-dom": "^16.3.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": {
    "/api": {
      "target": "http://localhost:8080",
      "ws": true
    }
  }
}

将以下内容添加到你的前端 App.js 文件中

import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

    state = {};

        componentDidMount() {
            this.dadJokes()
        }

    dadJokes = () => {
        fetch('/api/dadjokes')
            .then(response => response.text())
            .then(message => {
                this.setState({message: message});
            });
    };

    render() {
        return (
            <div className="App">
            <header className="App-header">
            <img src={logo} className="App-logo" alt="logo"/>
            <h3 className="App-title">{this.state.message}</h3>
            </header>
            <p className="App-intro">
            To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        </div>
    );
    }
}

export default App;

如果你也遇到了下图的这个错误,我的做法是:首先删除了 package-lock.json 文件;其次在 node_modules 文件夹中重新安装了 npm 包并再次运行;然后重新启动前端,问题就解决了。

A4F192DD_9600_4a0d_ADAD_0BFF94FD002C

9.你的应用程序现在应该看起来像这样。

你可以看到 dad jokes API 调用的结果。

1513E9D1_E5C2_4885_8A59_86AE5D3F0DC1

10.创建生产版本

现在我们的基本前端和后端已经完成,该创建生产版本和单个 war 文件了。

< dependencies>下添加

<!-- https://mvnrepository.com/artifact/com.github.eirslett/frontend-maven-plugin -->
<dependency>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
</dependency>

在 pom 文件的< plugins>部分下,我们将添加以下命令,这些命令将在运行 mvn clean install 时执行以下操作。

  • npm 安装指定版本的 node
  • 运行我们前端的生产构建
  • 存放生产构建
<plugin>
   <groupId>com.github.eirslett</groupId>
   <artifactId>frontend-maven-plugin</artifactId>
   <version>1.6</version>
   <configuration>
      <workingDirectory>basic-frontend-app</workingDirectory>
      <installDirectory>target</installDirectory>
   </configuration>
   <executions>
      <execution>
         <id>install node and npm</id>
         <goals>
            <goal>install-node-and-npm</goal>
         </goals>
         <configuration>
            <nodeVersion>v8.9.4</nodeVersion>
            <npmVersion>5.6.0</npmVersion>
         </configuration>
      </execution>
      <execution>
         <id>npm install</id>
         <goals>
            <goal>npm</goal>
         </goals>
         <configuration>
            <arguments>install</arguments>
         </configuration>
      </execution>
      <execution>
         <id>npm run build</id>
         <goals>
            <goal>npm</goal>
         </goals>
         <configuration>
            <arguments>run build</arguments>
         </configuration>
      </execution>
   </executions>
</plugin>
<plugin>
   <artifactId>maven-antrun-plugin</artifactId>
   <executions>
      <execution>
         <phase>generate-resources</phase>
         <configuration>
            <target>
               <copy todir="${project.build.directory}/classes/public">
                  <fileset dir="${project.basedir}/basic-frontend-app/build"/>
               </copy>
            </target>
         </configuration>
         <goals>
            <goal>run</goal>
         </goals>
      </execution>
   </executions>
</plugin>

附注:对于你的插件来说,顺序正确是很重要的,因此请确保在复制构建文件执行之前执行 node/npm 安装

11.运行 mvn clean install

添加此命令后,运行 mvn clean install,并验证 target/classes 目录同时包含了前端文件和 Java 文件。这里你应该是一路畅通的。

最后看一下我的 pom 文件:

https://github.com/Emmanuella-Aninye/Spring-Boot-ReactJS-Starter/blob/master/pom.xml

这就是我的方法。如果你想看看 repo 或使用它,可以在我的 Github 上找到它:

https://github.com/Emmanuella-Aninye/Spring-Boot-ReactJS-Starter

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-03-31
本文作者:Emmanuella
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ

相关文章
|
27天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
40 0
|
1月前
|
监控 Java 数据处理
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
|
2月前
|
Java 开发工具 Maven
springboot项目打包为sdk供其他项目引用
springboot项目打包为sdk供其他项目引用
|
26天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
59 1
|
6天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
24 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
8天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
【Spring系列】Sping VS Sping Boot区别与联系
|
21天前
|
存储 安全 Java
Spring Security应用讲解(Java案列演示)
Spring Security应用讲解(Java案列演示)
|
26天前
|
Prometheus 监控 Cloud Native
Spring Boot 应用可视化监控
Spring Boot 应用可视化监控
15 0
|
1月前
|
SQL 监控 Java
nacos常见问题之dubbo+nacos+springboot3的native打包成功后运行出现异常如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
36 2
|
1月前
|
jenkins Java 持续交付
详解如何使用Jenkins一键打包部署SpringBoot项目
详解如何使用Jenkins一键打包部署SpringBoot项目
72 0