background
For a single application, the front end uses the React framework, and static resources (JS, CSS, etc.) are placed under the src\main\resources\static Directory:
.babelrc .gitignore .mvn node_modules package-lock.json package.json pom.xml src --main --java --resources --static --templates --dist webpack.config.js
The front-end build configuration file webpack config. js:
var path = require('path') var webpack = require('webpack') var HtmlWebpackPlugin = require('html-webpack-plugin') let devServer = {} let plugins = [ new webpack.DefinePlugin({ ISDEV: JSON.stringify(process.env.NODE_ENV === 'development'), }), new HtmlWebpackPlugin({ template: "./src/main/resources/templates/index.html", // ? inject: true }), ] if (process.env.NODE_ENV === 'development') { devServer = { contentBase: path.join(__dirname, './src/main/resources/templates/dist'), host: 'localhost', port: '8000', open: true, // Automatically pull up the browser hot: true, // Thermal loading } plugins.push(new webpack.HotModuleReplacementPlugin()) } module.exports = { entry: './src/main/resources/static/index.js', output: { path: path.resolve(__dirname, './src/main/resources/templates/dist'), publicPath: process.env.NODE_ENV === 'development' ? '' : './templates/dist/', filename: process.env.NODE_ENV === 'development' ? 'build.js' : 'build.[chunkhash].js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: [{ loader: 'babel-loader', options: { presets: ['@babel/preset-env', "@babel/preset-react"], plugins: ['@babel/plugin-proposal-object-rest-spread', "@babel/plugin-proposal-function-bind", '@babel/plugin-proposal-class-properties'] } }, // { // Loader: 'eslint loader', / / specify to enable eslint loader // options: { // formatter: require('eslint-friendly-formatter'), // emitWarning: false // } // } ] }, { test: /\.(scss|css|less)$/, exclude: /node_modules/, // It is used for custom style processing, and node needs to be removed_ Modules folder use: [{ loader: "style-loader" }, { loader: "css-loader", options: { modules: true, // Add styles in the way of className={styles.xxx} localIdentName: '[local]--[hash:base64:5]' } }, { loader: "less-loader", }] }, { test: /\.(scss|css|less)$/, include: /node_modules/, // For antd, including node_modules use: [{ loader: "style-loader" }, { loader: "css-loader", }, { loader: "less-loader", options: { modifyVars: { // Custom theme color "primary-color": "#6E54B0", "link-color": "#6E54B0", 'border-radius-base': '2px', }, javascriptEnabled: true, } }] }, { test: /\.(eot|ttf|woff|woff2)(\?\S*)?$/, loader: 'file-loader' }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { extensions: ['.js', '.jsx', '.json'], }, performance: {hints: false}, plugins: plugins, devServer }
Front end package JSON files are nothing special:
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "set NODE_ENV=development&& webpack --mode=development", "pre": "set NODE_ENV=development&& webpack --mode=development", "prod": "set NODE_ENV=production&& webpack --mode=production", "start": "set NODE_ENV=development&&webpack-dev-server --mode development --port=8001" } }
The backend has a configuration file staticresourcesconfig java:
@Configuration public class StaticResourcesConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/"); super.addResourceHandlers(registry); } // Regardless of this topic, solve cross domain configuration @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") .maxAge(3600) .allowCredentials(true); } }
Now, if you want to separate the front and rear ends, this is the background.
practice
Project structure adjustment:
Root directory POM xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.aaa.framework.microservice</groupId> <artifactId>microservice-starter-parent</artifactId> <version>1.1.9</version> </parent> <groupId>com.aaa.cbd.platform</groupId> <artifactId>octopus-backend-parent</artifactId> <packaging>pom</packaging> <version>1.0.0-SNAPSHOT</version> <modules> <module>octopus-backend-ui</module> <module>octopus-backend</module> </modules> <properties> <module.version>1.0.0-SNAPSHOT</module.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.aaa.cbd.platform</groupId> <artifactId>octopus-backend</artifactId> <version>${module.version}</version> </dependency> <dependency> <groupId>com.aaa.cbd.platform</groupId> <artifactId>octopus-backend-ui</artifactId> <version>${module.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <verbose>true</verbose> <fork>true</fork> <compilerVersion>${java.version}</compilerVersion> <source>${java.version}</source> <target>${java.version}</target> <encoding>UTF8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
Backend POM The XML file depends on the front-end build product Jar:
<project> <parent> <groupId>com.aaa.cbd.platform</groupId> <artifactId>octopus-backend-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>octopus-backend</artifactId> <properties> <octopus-backend-ui.version>1.0.0-SNAPSHOT</octopus-backend-ui.version> </properties> <dependencies> <dependency> <groupId>com.aaa.cbd.platform</groupId> <artifactId>octopus-backend-ui</artifactId> <version>${octopus-backend-ui.version}</version> </dependency> </dependencies> </project>
The POM of front-end engineering The XML file configuration is as follows:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.aaa.cbd.platform</groupId> <version>1.0.0-SNAPSHOT</version> <artifactId>octopus-backend-ui</artifactId> <properties> <build-plugin.exec.version>1.4.0</build-plugin.exec.version> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <executions> <execution> <id>exec-npm-install</id> <phase>validate</phase> <goals> <goal>exec</goal> </goals> <configuration> <executable>npm</executable> <arguments> <argument>install</argument> </arguments> </configuration> </execution> <execution> <id>npm-build</id> <phase>generate-resources</phase> <goals> <goal>exec</goal> </goals> <configuration> <executable>npm</executable> <arguments> <argument>run</argument> <argument>build</argument> </arguments> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>woff</nonFilteredFileExtension> <nonFilteredFileExtension>ttf</nonFilteredFileExtension> <nonFilteredFileExtension>woff2</nonFilteredFileExtension> <nonFilteredFileExtension>eot</nonFilteredFileExtension> <nonFilteredFileExtension>swf</nonFilteredFileExtension> <nonFilteredFileExtension>ico</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin> </plugins> <resources> <resource> <directory>dist</directory> <targetPath>META-INF/ui</targetPath> <filtering>true</filtering> </resource> </resources> <pluginManagement> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>${build-plugin.exec.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> </plugin> </plugins> </pluginManagement> </build> </project>
Front end webpack config. JS only lists the changed parts:
let plugins = [ new HtmlWebpackPlugin({ // change 1 template: "./index.html", }), ] if (process.env.NODE_ENV === 'development') { devServer = { // change 2 contentBase: path.join(__dirname, './dist'), host: 'localhost', port: '8000', open: true, hot: true, } plugins.push(new webpack.HotModuleReplacementPlugin()) } module.exports = { // change 3 entry: './src/index.js', output: { // change 4 path: path.resolve(__dirname, './dist'), // change 5 publicPath: process.env.NODE_ENV === 'development' ? '' : './', filename: process.env.NODE_ENV === 'development' ? 'build.js' : 'build.[chunkhash].js' } }
According to the pom file configuration of the parent project, open the maven panel, execute clean and then install:
The following construction products can be obtained:
It can be found that the front-end project will be packaged as a Jar and placed as a dependency in the Jar package of the back-end Spring Boot application.
At this point, we can start the application: Java - Jar - dserver port=8082 octopus-backend-1.0. 0-SNAPSHOT. jar
Browser input: http://localhost:8082/hs , return the response OK. Because we write an application health Controller interface:
@RestController public class HealthController { @RequestMapping(value = "/hs") public String hs() { return "OK"; }
The question is, how to access the front-end page?
The answer is: StaticResourcesConfig configuration file.
The previous configuration was:
registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
We replace with:
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/META-INF/");
At the same time, POM The XML file is updated to:
<resources> <resource> <directory>dist</directory> <targetPath>META-INF/static</targetPath> <filtering>true</filtering> </resource> <resources>
Then enter in the browser: http://localhost:8082/static/static/index.html#/ , you can access:
How to remove two layers of static nesting?
For the attempt of blood and tears, ResourceHandler must be a two-tier Directory:
registry.addResourceHandler("/**/**").addResourceLocations("classpath:/META-INF/");
At the same time, POM The XML file is updated to:
<resources> <resource> <directory>dist</directory> <targetPath>META-INF/</targetPath> <filtering>true</filtering> </resource> </resources>
Unzip the front end engineering Jar package:
The dist folder was found and copied to the META-INF directory.
New problems
The browser opens the login page
http://localhost:8082/index.html#/
After successful login, enter the home page: 404
http://localhost:8082/index
The first page that can actually be displayed normally:
http://localhost:8082/index.html#/index
Other pages:
http://localhost:8082/index.html#/appList
By implication, the page needs to be nested with another layer of index html#/
How to solve this problem? It seems that the problem is the front-end path. At this time, if you focus on the front-end build file webpack config. JS is a detour.
The final solution is to configure the StaticResourcesConfig file:
package config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class StaticResourcesConfig extends WebMvcConfigurerAdapter { @Value("${server.contextPath:}") private String contextPath; @Value("${applications.indexForwardPath:index.html}") private String forwardPath; @Value("${applications.resourceLocations:classpath:/static/,classpath:/public/}") private String resourceLocations; @Override public void addViewControllers(ViewControllerRegistry registry) { // contextPath is null, if condition does not hold if (StringUtils.hasText(contextPath)) { registry.addRedirectViewController(contextPath, contextPath + "/"); } // What are the differences between the following two addviewcontrollers registry.addViewController(contextPath + "/").setViewName(forwardPath); registry.addViewController("/*/").setViewName("forward:index.html"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { resourceLocations += ",classpath:/META-INF/"; registry.addResourceHandler("/**") .addResourceLocations(resourceLocations.split(",")) .resourceChain(true); } }
In addition, remove a Controller:
@RequestMapping("") @Controller public class UserController { @GetMapping public String goToLogin() { return "dist/index"; } }