解决.env.production 写死 IP 的问题:Vue + config.json 运行时加载方案
背景:前端常用 .env.production 在构建时写死 API 地址
场景:运维部署时经常不知道目标主机 IP/域名
问题:每次 IP 变动都要重新编译 → 增加运维成本
引出需求:只修改 IP 就能完成部署,不需要重新打包
目录
- 一、解决方案思路
- 二、实现步骤
- 三、打包部署
- 四、知识补充
一、解决方案思路
1、前端层面
- 把动态配置从 .env.production 抽离,放到 public/config.json
- 在应用启动时 fetch 该文件 → 写入 window.__APP _ CONFIG __
- 业务代码改用 window.__APP _ CONFIG __ 或封装好的 axios 实例获取 baseURL
2、后端层面(Spring Boot)
- Spring Security 默认拦截所有请求,导致 /config.json 也被保护 → 返回 401
- 在 security 配置中将 /config.json 加入 permitAll 白名单,确保前端静态资源匿名可访问
二、实现步骤
- 新建 public/config.json(存放运行时配置)
{"ENV": "production","VUE_APP_BASE_API": "","VUE_APP_GPU_BASE_URL": "http://10.143.135.91:9001","VUE_APP_AUTO_LOGIN_USER": "admin","VUE_APP_AUTO_LOGIN_PWD": "21232f297a57a5a743894a0e4a801fc3"
}
- 修改 main.js → 应用启动前加载 config.json
在 src/bootstrapConfig.ts 中封装 loadRuntimeConfig
✅ Vue 3(Vite 或 Vue CLI)
src/bootstrapConfig.ts(JS 项目就用 .js)
export type AppConfig = {ENV: stringVUE_APP_BASE_API: stringVUE_APP_GPU_BASE_URL: stringVUE_APP_AUTO_LOGIN_USER: stringVUE_APP_AUTO_LOGIN_PWD: string
}export async function loadRuntimeConfig(): Promise<AppConfig> {const res = await fetch('./config.json', { cache: 'no-store' })if (!res.ok) throw new Error(`load config.json failed: ${res.status}`)const cfg = (await res.json()) as AppConfig;(window as any).__APP_CONFIG__ = cfgreturn cfg
}
export function getConfig(): AppConfig {return (window as any).__APP_CONFIG__ as AppConfig
}
src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { loadRuntimeConfig } from './bootstrapConfig'async function bootstrap() {await loadRuntimeConfig()createApp(App).mount('#app')
}
bootstrap()
✅ Vue 2(Vue CLI)
src/bootstrapConfig.js
export async function loadRuntimeConfig() {const res = await fetch('./config.json', { cache: 'no-store' })if (!res.ok) throw new Error('load config.json failed: ' + res.status)window.__APP_CONFIG__ = await res.json()
}
export function getConfig() { return window.__APP_CONFIG__ }
src/main.js
import Vue from 'vue'
import App from './App.vue'
import { loadRuntimeConfig } from './bootstrapConfig'async function bootstrap() {await loadRuntimeConfig()new Vue({ render: h => h(App) }).$mount('#app')
}
bootstrap()
- axios 封装:统一读取 ‘__APP_CONFIG __’ 作为 baseURL
src/api/http.ts
import axios from 'axios'const api = axios.create({baseURL: (window as any).__APP_CONFIG__.VUE_APP_BASE_API
})export default api
业务里:
import api from '@/api/http'
// api.get('/xxx')
另一种方式
使用${window.__APP_CONFIG __.VUE_APP_GPU_BASE_URL}
- Spring Security 配置修改:放行 /config.json
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {........@Overrideprotected void configure(HttpSecurity http) throws Exception {List<String> defaultExcludes = new ArrayList<>();defaultExcludes.add("/");defaultExcludes.add("/#/**");defaultExcludes.add("/static/**");defaultExcludes.add("/swagger-ui.html");defaultExcludes.add("/swagger-ui/**");defaultExcludes.add("/swagger-resources/**");defaultExcludes.add("/doc.html");defaultExcludes.add("/doc.html#/**");defaultExcludes.add("/v3/api-docs/**");defaultExcludes.add("/index.html");defaultExcludes.add("/webjars/**");defaultExcludes.add("/js/**");defaultExcludes.add("/api/device/query/snap/**");defaultExcludes.add("/record_proxy/*/**");defaultExcludes.add("/api/emit");defaultExcludes.add("/favicon.ico");defaultExcludes.add("/api/user/login");defaultExcludes.add("/index/hook/**");defaultExcludes.add("/api/device/query/snap/**");defaultExcludes.add("/index/hook/abl/**");// 👇 新增这一行,允许匿名访问 config.jsondefaultExcludes.add("/config.json");
部署到Spring Boot → 验证只改 config.json 就能适配 IP
注意:改造后的文件结构
- .env.production (最小化)
# just a flag
NODE_ENV=production
VUE_APP_BUILD_VERSION=1.0.0
⚠️ 注意:
VUE_APP_BASE_API、GPU_BASE_URL、账号密码这些 删掉,因为它们要 runtime 配置,不要在这里写。
可以额外放一些编译时常量(例如版本号、构建时间)。
三、打包部署
方案一
在yml文件下添加配置规则,能够读取容器内的文件
spring:web:resources:static-locations: file:/app/static/,classpath:/static/
方案二
在Dockerfile配置文件
ENTRYPOINT ["java", \"-Dspring.web.resources.static-locations=file:/app/static/,classpath:/static/", \"-Dspring.resources.static-locations=file:/app/static/,classpath:/static/", \"-jar","wvp.jar", \"--spring.config.location=/app/config/"]
👉 -Dspring.resources.static-locations 是 Spring Boot 2.3 及之前的老配置项,
👉 -Dspring.web.resources.static-locations 是 Spring Boot 2.4 及之后的新配置项,作用完全相同,只是命名空间不同。
意思是:
- 覆盖默认规则(自己指定顺序)。
- 让 Spring Boot 在两个地方找静态资源:
file:/app/static/ → 容器里的目录(来自宿主机挂载的 /app/static)。
classpath:/static/ → 打包在 jar 里的 resources/static/。
👉 关键点是:顺序有优先级,Spring Boot 会先从 file:/app/static/ 找,如果找不到,再去 classpath:/static/。
挂载文件(先copy一份到宿主机 然后挂载到容器内部/app/static/)
然后就会首先读取容器里面的IP
修改之后重启容器
docker compose up -d --force-recreate
四、知识补充
Spring Boot 静态资源路径设置
如果你不写任何配置,Spring Boot 会自动从以下目录里找静态文件(按顺序):
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/ ✅ 最常见(即你项目里的 src/main/resources/static)
- classpath:/public/
举个例子:
你把 src/main/resources/static/config.json 打包进 jar → 浏览器访问 http://localhost:8080/config.json 就能拿到。
这是因为 classpath:/static/ 在默认搜索路径里。