导读:本期聚焦于林小满创作的《Vue中如何动态配置域名以解决不同环境下的跨域问题?》,敬请观看详情。接口域名写死在前端代码里,换一次测试环境就要重新打包一次,这种场景在中小型Vue项目里相当常见。跨域问题本身不是浏览器不允许请求,而是响应回来后被同源策略拦下,表现出来就是控制台里的CORS报错。解决方式可以分两条线:本地开发用Vite或Vue CLI的代理把接口路径转到目标服务器,生产环境则让接口和前端同源,或者由后端显式配置跨域白名单。麻烦之处在于不同环境接口域名不同,手动改很容易出错。因此更稳的做法是把域名放进环境变量,再通过axios拦截器或运行时配置动态注入,实现开发、测试、预发、生产自动切换。这篇文章会从跨域成因、动态域名配置的具体实现、Nginx和后端需要注意的细节几个角度展开,重点说明路径重写、环境变量失效和凭证请求等容易忽略的坑。

前后端分离的项目里,Vue应用通常跑在本地开发服务器的某个端口上,例如 http://localhost:5173,而接口服务可能部署在 http://api.ipipp.com。浏览器同源策略会拦截不同源之间的响应读取,这就是控制台里频繁出现 CORS 报错的根本原因。很多团队处理跨域时习惯在后端临时返回通配符 Access-Control-Allow-Origin: *,开发阶段确实省事,但到了测试、预发、生产等多个环境,接口域名各不相同,前端如果把地址写死,切换环境就要改代码,容易出错也不利于自动化发布。合理的做法是让接口域名根据环境动态注入,开发环境同时配合代理绕过浏览器限制,生产环境再结合 Nginx 或后端 CORS 白名单完成闭环。

Vue中如何动态配置域名以解决不同环境下的跨域问题?

一、跨域的本质与开发环境代理思路

浏览器同源策略要求页面脚本只能读取同源响应。这里的同源指的是协议、域名、端口三者完全一致,例如 http://localhost:5173 和 http://localhost:3000 端口不同,就属于跨域。很多开发者误以为跨域是请求没有到达服务器,实际上大多数情况下后端已经正常处理了请求,只是浏览器在拿到响应后发现响应头里没有允许当前源读取的字段,于是把结果丢弃,并在控制台打印 CORS 错误。对于简单 GET、POST 请求,浏览器不会发送预检请求;但对于携带自定义请求头、使用 PUT 或 DELETE 方法、或 Content-Type 为 application/json 的请求,浏览器会先发送一个 OPTIONS 预检请求,确认服务器明确允许这些方法后才继续真实请求。

Vue 官方脚手架内置的开发服务器底层都是 Node HTTP 服务,可以非常方便地开启 proxy 代理。代理的核心思路是:前端代码只请求当前开发服务器的相对路径,例如 /api/user,这个请求对浏览器而言是同源的,因此不会触发跨域限制。开发服务器收到请求后,在 Node 层把请求转发到真正的后端地址,例如 http://localhost:3000/api/user,拿到响应后再返回给浏览器。这样浏览器从头到尾只跟同一个开发服务器通信,跨域问题自然就不存在了。

先创建环境变量文件,把开发环境的接口前缀和目标地址分开管理:

# .env.development
VITE_API_BASE_URL=/api
VITE_TARGET_ORIGIN=http://localhost:3000

接着在 Vite 的配置文件中根据变量启用代理:

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')
  return {
    plugins: [vue()],
    server: {
      proxy: {
        '/api': {
          target: env.VITE_TARGET_ORIGIN,
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        }
      }
    }
  }
})

这里有两个配置非常关键。changeOrigin 会把转发请求的 Host 头改成目标服务器的域名,避免某些后端根据 Host 做校验时拒绝请求。rewrite 则负责路径重写,如果后端接口本身不带 /api 前缀,就需要把请求路径中的 /api 去掉,否则会出现 404。开发代理只对本地开发环境有效,打包后的静态文件不会经过 Vite 开发服务器,因此生产环境还需要单独处理。

二、多环境动态域名配置的核心实现

不同环境接口域名不同,如果让开发、测试、预发和生产共用一份构建配置,就需要把接口地址抽离成环境变量。Vite 项目默认会读取项目根目录下的 .env、.env.development、.env.production 等文件,只有以 VITE_ 开头的变量才会暴露给客户端代码。读取方式统一为 import.meta.env.VITE_XXX。如果项目使用的是 Vue CLI,则变量前缀是 VUE_APP_,代码中通过 process.env.VUE_APP_XXX 读取,两者思路完全一致,只是前缀不同。

以 Vite 项目为例,分别给开发、测试和生产定义不同的接口地址:

# .env.development
VITE_API_BASE_URL=/api

# .env.test
VITE_API_BASE_URL=https://test-api.ipipp.com

# .env.production
VITE_API_BASE_URL=https://api.ipipp.com

然后在 axios 实例中读取该变量,不要在任何组件里直接写接口地址,这样就能实现按构建模式自动切换:

import axios from 'axios'

const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
})

service.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = 'Bearer ' + token
  }
  return config
})

service.interceptors.response.use(
  response => response.data,
  error => Promise.reject(error)
)

export default service

环境变量方案适合每个环境单独构建的场景,但如果你想用同一份构建产物部署到多个环境,就需要引入运行时配置。可以把配置文件放到 public 目录,这样构建时不会被 Vite 打包处理,部署时由运维或脚本替换其中的配置。文件内容可以形如:

// public/config.js
window.__APP_CONFIG__ = {
  API_BASE_URL: '/api'
}

之后在 index.html 中通过 <script src="/config.js"></script> 引入,axios 实例读取时优先使用运行时配置,找不到再退回环境变量:

const runtimeBaseURL = window.__APP_CONFIG__?.API_BASE_URL
const fallbackBaseURL = import.meta.env.VITE_API_BASE_URL

const service = axios.create({
  baseURL: runtimeBaseURL || fallbackBaseURL,
  timeout: 10000
})

这种做法的好处是同一份包可以在不同环境直接部署,不需要反复打包,也方便测试同学在部署机器上临时调整接口地址。但它也有一个前提:配置文件必须在 Vue 应用加载前完成加载,否则 window.__APP_CONFIG__ 是 undefined。因此引入脚本时不要使用异步加载,也不要把配置文件放在会被 Hash 路由影响的路径里。两种方案可以按团队发布流程选择,业务复杂时也可以同时保留,环境变量作为构建时默认值,运行时配置作为兜底覆盖。

三、生产环境配合 Nginx 与常见注意事项

开发环境可以靠代理绕过跨域,但生产环境需要更彻底的处理方式。如果前端静态资源和后端接口可以部署在同一个域名下,最推荐的做法是用 Nginx 托管前端文件,并把 /api 路径反向代理到后端服务。浏览器仍然只跟同一个源通信,从架构上直接消除跨域问题。一个基础的配置如下:

server {
    listen 80;
    server_name www.ipipp.com;

    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://backend-server:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

如果前后端确实必须使用不同域名,比如前端在 www.ipipp.com,接口在 api.ipipp.com,那就需要后端配置 CORS 响应头。以 Node.js 后端为例,可以统一在中间件里设置允许的前端源:

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://www.ipipp.com')
  res.setHeader('Access-Control-Allow-Credentials', 'true')
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS')
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization')
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204)
  }
  next()
})

实际落地时还有几个高频坑要留意。第一,修改 .env 文件后必须重启开发服务器,否则 Vite 不会重新加载环境变量。第二,如果 axios 的 baseURL 配置成了完整的绝对地址,例如 http://localhost:3000,请求就不会匹配 Vite 的代理规则,跨域依然存在。第三,前端如果开启了 withCredentials 携带 Cookie,后端不能使用 Access-Control-Allow-Origin: *,必须明确指定来源域名,并且同时返回 Access-Control-Allow-Credentials: true。第四,预检请求会被浏览器缓存,修改 CORS 配置后如果仍然报错,可以先尝试无痕窗口或强制刷新。第五,Nginx 中 location /api/ 和 proxy_pass 末尾的斜杠会直接影响最终拼接路径,出现 404 或 403 时优先检查路径是否重复或缺失。第六,线上页面使用 HTTPS 时,接口地址绝对不能使用 HTTP,否则浏览器会直接拦截混合内容请求,这已经不是跨域问题,而是安全策略问题。

动态配置域名的最终目的不是单纯解决某一个环境的跨域报错,而是让 Vue 项目在不同环境中都能使用一套稳定的请求层代码。环境变量负责构建时切换,运行时配置负责部署时调整,开发代理负责本地调试,生产环境则通过 Nginx 同源转发或后端 CORS 白名单兜底。把这些环节组合起来,才能让多环境开发、发布和排错都变得可控。

Vue跨域配置动态域名环境变量修改时间:2026-09-24 16:50:48

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0924/61386.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。