先讲一讲这篇文章的背景故事。之前我的团队需要在我们需求的基础架构上节省一些资金,并且由于我们要构建的这个应用程序中,大部分负载都会在客户端而非服务端上,所以我们决定试验一下能否将一个 Spring 应用程序与一个 React 应用结合起来,并打包成一个 war 文件。
这篇文章会告诉你如何将 Create React App 与 Spring Boot 结合使用,从而为你打包出单个 war 文件。
附注:我选择的 IDE 是 IntelliJ。当使用 React 代码时,我通常会切换到 VS Code。你可以随意使用自己习惯的方法
GroupId:e.the.awesome
Artifact:spring-react-combo-app
你的 SpringReactComboAppApplication 看起来应该像这样。
复制代码
packagee.the.awesome.springreactcomboapp; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication publicclassSpringReactComboAppApplicationextendsSpringBootServletInitializer{ 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 publicclassDadJokesController{ @GetMapping("/api/dadjokes") public String dadJokes() { return"Justice is a dish best served cold, if it were served warm it would be just water."; } }
复制代码
mvnspring-boot:run
然后在浏览器中检查 http://localhost:8080/api/dadjokes 。你应该会看到我们添加到控制器中的 dad joke。
复制代码
npxcreate-react-appbasic-frontend-app
你可以随心所欲地调用它,我这里只调用我的 basic-frontend-app
复制代码
cdbasic-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 文件中
复制代码
importReact, {Component}from'react'; importlogofrom'./logo.svg'; import'./App.css'; classAppextendsComponent{ state = {}; componentDidMount() { this.dadJokes() } dadJokes =()=>{ fetch('/api/dadjokes') .then(response=>response.text()) .then(message=>{ this.setState({message: message}); }); }; render() { return( <divclassName="App"> <headerclassName="App-header"> <imgsrc={logo}className="App-logo"alt="logo"/> <h3className="App-title">{this.state.message}</h3> </header> <pclassName="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 文件了。
复制代码
<!-- 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> <copytodir="${project.build.directory}/classes/public"> <filesetdir="${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