先讲一讲这篇文章的背景故事。之前我的团队需要在我们需求的基础架构上节省一些资金,并且由于我们要构建的这个应用程序中,大部分负载都会在客户端而非服务端上,所以我们决定试验一下能否将一个Spring应用程序与一个React应用结合起来,并打包成一个war文件。
这篇文章会告诉你如何将Create React App与Spring Boot结合使用,从而为你打包出单个war文件。
Spring Boot和Create React App的基本功能介绍
附注:我选择的IDE是IntelliJ。当使用React代码时,我通常会切换到VS Code。你可以随意使用自己习惯的方法
GroupId:e.the.awesome
Artifact:spring-react-combo-app
你的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);
}
}
我们将其称为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.";
}
}
mvn spring-boot:run
然后在浏览器中检查http://localhost:8080/api/dadjokes。你应该会看到我们添加到控制器中的dad joke。
npx create-react-app basic-frontend-app
你可以随心所欲地调用它,我这里只调用我的basic-frontend-app
cd basic-frontend-app<br>npm start
由于我们要将Dad Jokes服务集成到前端,因此首先我们要解决代理问题。你可能已经注意到了,你的服务从localhost:8080开始,而前端从localhost:3000开始。如果我们尝试从前端调用服务,具体取决于你的浏览器,你可能会收到一个CORS错误。
解决此问题的最简单方法是让前端代理处理从端口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包并再次运行;然后重新启动前端,问题就解决了。
你可以看到dad jokes API调用的结果。
现在我们的基本前端和后端已经完成,该创建生产版本和单个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时执行以下操作。
<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安装
添加此命令后,运行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://hackernoon.com/package-your-react-app-with-spring-boot-a-how-to-guide-cdfm329w
领取专属 10元无门槛券
私享最新 技术干货