父目录
项目结构
添加pom文件
<?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>
<groupId>com.父目录名</groupId>
<artifactId>父目录名</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>父目录项目名</name>
<description>改成自己的项目名</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.15</version>
<relativePath />
</parent>
<modules>
<module>前端名</module>
<module>后端名</module>
</modules>
<properties>
<spring.boot.version>2.5.15</spring.boot.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中-->
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
后端
pom文件添加打包插件:
<!-- 插件maven-clean-plugin,用于在编译前,清除之前编译的文件、文件夹等,避免残留之前的内容 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<!-- 前端资源目录,即:存放前端包目录-->
<directory>src/main/resources/static</directory>
</fileset>
<fileset>
<!-- Vue项目打包自动生成的dist目录 -->
<directory>${project.parent.basedir}/vue-ui/dist</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<!--frontend-maven-plugin为项目本地下载/安装Node和NPM,运行npm install命令-->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<workingDirectory>${project.parent.basedir}/vue-ui</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<!-- 改成对应版本 -->
<configuration>
<nodeVersion>v16.13.0</nodeVersion>
<npmVersion>8.1.0</npmVersion>
</configuration>
</execution>
<!-- Install all project dependencies -->
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<!-- Build and minify static files -->
<execution>
<id>npm run build:prod</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build:prod</arguments>
</configuration>
</execution>
</executions>
</plugin>
<!--资源插件,主要为了从前端项目里复制打包好的文件到springboot项目-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy static</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<!-- 复制前端打包文件到这里 -->
<outputDirectory>src/main/resources/static</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<!-- 从前端打包的目录dist进行指定文件、文件夹内容的复制-->
<directory>${project.parent.basedir}/vue-ui/dist</directory>
<includes>
<!-- 具体根据实际前端代码、及目录结构进行配置-->
<include>static/css/</include>
<include>static/fonts/</include>
<include>static/img/</include>
<include>static/js/</include>
<include>favicon.ico</include>
<include>index.html</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
ResourcesConfig.java添加页面静态化和首页规则配置
package com.pinnacles.framework.config;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.CacheControl;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.pinnacles.common.constant.Constants;
import com.pinnacles.framework.interceptor.RepeatSubmitInterceptor;
/**
* 通用配置
*
* @author casoft
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
@Autowired
private RepeatSubmitInterceptor repeatSubmitInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
.addResourceLocations("file:" + CaSoftConfig.getProfile() + "/");
/** 页面静态化 */
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/static/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
}
/*首页规则*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("index.html");
registry.addViewController("/").setViewName("index.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
/**
* 自定义拦截规则
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
}
/**
* 跨域配置
*/
@Bean
public CorsFilter corsFilter()
{
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 1800秒
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
SecurityConfig.java放开静态资源请求拦截
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET,"/**/**","/**","/index", "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 放开Swaggger文档
.antMatchers("/doc.html","/static/**").anonymous()
// 放开WebSocket
.antMatchers("/websocket/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
}
前端
添加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>
<artifactId>父目录的pom名</artifactId>
<groupId>com.父目录</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.父目录.vue-ui</groupId>
<artifactId>vue-ui</artifactId>
</project>
修改.env.production文件
注释之后页面刷新不出现404问题
# 直接注释掉
# VITE_APP_ENV = 'production'
# 直接注释掉或者改为 '/' 都行
# VITE_APP_BASE_API = '/prod-api'
vite.config.js 打包未分包的需添加分包规则
与后端插件一起使用,按自己的打包结构来自行调整
build:{
rollupOptions:{
output:{
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: (assetInfo) => {
if (assetInfo.type === 'asset' && /\.(jpe?g|png|gif|svg)$/i.test(assetInfo.name)) {
return 'static/img/[name].[hash][ext]';
} if (assetInfo.type === 'asset' && /\.(ttf|woff|woff2|eot)$/i.test(assetInfo.name)) {
return 'static/fonts/[name].[hash][ext]';
}
return 'static/[ext]/name1-[hash].[ext]';
},
//manualChunks 两种使用形式
// manualChunks:{
// elementPlus:['element-plus']
// }
manualChunks(id) {
if (id.includes('element-plus')) {
return 'element-plus';
}
}
}
}
}
src/router/index.js 修改history
保证页面刷新不丢失
const router = createRouter({
//将createWebHistory() 改为 createWebHashHistory()
// history: createWebHistory(),
history: createWebHashHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
});
完成后,后端点击maven插件自带的package即可自动完成前后端打包+整合