父目录

项目结构

添加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即可自动完成前后端打包+整合

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐