Vue + Spring Boot 项目添加 /wvp 前缀的完整链路解析(从浏览器到静态资源)

文章目录

在 Vue + Spring Boot 项目中,为前端统一添加访问前缀(如 /wvp)是一个非常常见的需求,尤其在 多项目部署、反向代理、统一网关 的场景下。

很多人只改了前端配置,却遇到 404、白屏、资源加载失败 等问题,根本原因在于:
/wvp 必须在浏览器、Vue、Spring Boot 三个层面同时生效。

本文从访问链路 的角度,完整梳理 /wvp 前缀是如何一步步生效的。


一、最终访问效果

修改前:

复制代码
http://localhost:9528/#/dashboard

修改后:

复制代码
http://localhost:9528/wvp/#/dashboard

二、整体访问链路

Vue 不是在 Spring Boot 里跑的,
Vue 是在浏览器里跑的。

Spring Boot 只负责"把文件给浏览器"
不负责"页面怎么跳转、怎么渲染"。

第 1 步:你在浏览器地址栏输入

复制代码
http://localhost:9528/wvp/#/dashboard

⚠️ 注意:

  • #/dashboard 不会发给后端

  • 浏览器真正发出的请求只有:

    GET /wvp


第 2 步:Spring Boot 收到 /wvp

Spring Boot 想一想:

"有人访问 /wvp

这是不是我认识的一个路径?"

如果你没写

java 复制代码
@GetMapping("/wvp")

没写静态资源映射

👉 Spring Boot 会说:

"我不认识 → 404 → /error → Whitelabel"


第 3 步:你写了 WvpIndexController

java 复制代码
@GetMapping({"/wvp", "/wvp/"})

现在 Spring Boot 明白了:

"哦,访问 /wvp

我要返回 index.html"

于是它做了一件事:

复制代码
把 classpath:/static/index.html
发给浏览器

第 4 步:浏览器拿到 index.html

这一步 Spring Boot 已经下线了

接下来全是浏览器的事。

浏览器打开 index.html,发现里面有:

html 复制代码
<script src="/wvp/static/js/app.js"></script>
<link href="/wvp/static/css/app.css">

第 5 步:浏览器继续请求 JS / CSS

浏览器自动发请求:

复制代码
GET /wvp/static/js/app.js
GET /wvp/static/css/app.css

第 6 步:Spring Boot 再次出场(静态资源)

这次 Spring Boot 看到了:

复制代码
/wvp/static/js/app.js

它对照 WebMvcConfig

java 复制代码
/wvp/static/**  →  classpath:/static/static/**

于是:

复制代码
classpath:/static/static/js/app.js

被正确返回给浏览器。


第 7 步:Vue 开始运行(关键!)

现在:

  • JS 下载完了
  • Vue 代码在 浏览器里执行

Vue 做的第一件事是:

js 复制代码
router.base = '/wvp/'

然后它再看:

复制代码
URL 里有 #/dashboard

于是 Vue 说:

"我要渲染 Dashboard 页面"

⚠️ 这一切都发生在浏览器里

⚠️ 不会再请求 Spring Boot


第 8 步:页面显示出来

复制代码
Dashboard.vue 被渲染
页面正常显示

🎉 到这里流程结束

复制代码
① 浏览器 → Spring Boot
   只干一件事:要文件

② 浏览器里跑 Vue
   只干一件事:渲染页面

三、浏览器这一层发生了什么?

用户在浏览器中访问:

复制代码
http://localhost:9528/wvp/#/dashboard

浏览器首先发起的请求其实是:

复制代码
GET /wvp/index.html

👉 这一步与 Vue 路由无关,只是普通的 HTTP 请求。


四、Spring Boot 如何找到 index.html?

1️⃣ 问题点

Spring Boot 默认只能识别:

复制代码
/index.html
/static/**

并不知道 /wvp/index.html 是什么


2️⃣ 解决方案:WebMvcConfig

java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        // /wvp/static/** → 前端静态资源
        registry.addResourceHandler("/wvp/static/**")
                .addResourceLocations("classpath:/static/static/");

        // /wvp/index.html 等入口文件
        registry.addResourceHandler(
                "/wvp/index.html",
                "/wvp/favicon.ico",
                "/wvp/config.json",
                "/wvp/libDecoder.wasm"
        ).addResourceLocations("classpath:/static/");
    }
}

3️⃣ 实际效果

浏览器访问 实际读取
/wvp/index.html classpath:/static/index.html
/wvp/static/js/app.js classpath:/static/static/js/app.js

👉 这是 /wvp 在后端真正"生效"的地方

兜底

java 复制代码
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * WVP 前端页面控制器
 * 处理 /wvp 和 /wvp/ 路径,返回 index.html
 * 处理 /wvp/config.json,返回配置文件
 *
 * @author system
 */
@RestController
public class WvpIndexController {

    @GetMapping({"/wvp", "/wvp/"})
    public void index(HttpServletResponse response) throws IOException {
        Resource resource = new ClassPathResource("static/index.html");
        response.setContentType(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8");
        StreamUtils.copy(resource.getInputStream(), response.getOutputStream());
    }

    @GetMapping("/wvp/config.json")
    public void configJson(HttpServletResponse response) throws IOException {
        Resource resource = new ClassPathResource("static/config.json");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
        StreamUtils.copy(resource.getInputStream(), response.getOutputStream());
    }
}

五、Vue 在 /wvp 场景下如何工作?

1️⃣ publicPath:资源路径前缀

js 复制代码
// vue.config.js
module.exports = {
  publicPath: '/wvp/'
}

作用:

  • 生成的 index.html
  • JS / CSS / 图片路径全部带 /wvp/

2️⃣ router.base:前端路由前缀

js 复制代码
const router = new VueRouter({
  base: '/wvp/',
  routes
})

作用:

  • /#/dashboard/wvp/#/dashboard
  • 代码中写的 /dashboard 自动加前缀

3️⃣ 路由跳转是否需要修改?

不需要。

js 复制代码
this.$router.push('/dashboard')

Vue Router 会自动解析为:

复制代码
/wvp/#/dashboard

👉 推荐始终使用相对路径,避免硬编码 /wvp


六、Spring Security 需要注意什么?

如果项目使用了 Spring Security,需要显式放行 /wvp/**

java 复制代码
.antMatchers("/wvp/**").permitAll()

否则会出现:

  • 403
  • 页面或静态资源无法加载

七、常见错误总结

错误点 表现
只改 Vue,不改后端 404 / 白屏
忘了 publicPath JS / CSS 加载失败
忘了 router.base 路由错乱
Security 未放行 403

八、一句话总结

/wvp 前缀不是"某一个配置"决定的,

而是 浏览器、Vue、Spring Boot 三方协作的结果

只有当:

  • Vue 知道 /wvp
  • Spring Boot 认识 /wvp
  • 静态资源能从 /wvp 映射到磁盘

整个系统才能真正稳定运行。

相关推荐
粟悟饭&龟波功14 小时前
【软考系统架构设计师】九、架构演化与维护
前端·后端·架构·系统架构·软件工程
Java程序员 拥抱ai14 小时前
SpringBoot + FFmpeg + Redis:视频转码、截图、水印异步处理平台搭建
spring boot·redis·ffmpeg
广州华水科技14 小时前
单北斗GNSS的变形监测应用是什么?主要用于大坝的安全监测吗?
前端
Hao_Harrision14 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| AnimatedCountdown(倒计时组件)
前端·typescript·react·tailwindcss·vite7
qq_4061761414 小时前
什么是模块化
开发语言·前端·javascript·ajax·html5
ljh57464911914 小时前
npm run build:prod 打包后,文件中的console.log 失效
前端·npm·node.js
不吃香菜56714 小时前
WebSocket 超细致完整用法讲解(含原理 + 前端 + 后端 + 实战案例 + 避坑)
前端·网络·websocket·网络协议
技术钱14 小时前
vue3 + element plus实现表头拖拽数组进行汇总
前端·javascript·vue.js
chenhdowue14 小时前
vue 如何实现 vxe-table 的按键操作回车键的上下移动修改为 Tab 键的左右切换
vue.js·vxe-table·vxe-ui