一、为什么会遇到跨域?
在前后端分离开发中,我们常遇到 跨域问题 :比如前端 Vue 项目运行在 http://localhost:8080
,后端接口部署在 http://localhost:5000
(或 5001),当前端用 Axios 请求后端接口时,浏览器会因 同源策略 拦截请求(同源需满足:协议、域名、端口三者一致)。
解决跨域的方案有很多,比如后端 CORS、JSONP 等,而 Vue 项目中最常用的是 配置本地代理服务器 ------ 利用 Vue CLI 的devServer.proxy
,让代理服务器(和前端同端口 8080)转发请求到后端,由于服务器之间不存在同源限制,从而绕过跨域拦截。
二、环境准备
-
安装 Axios :用于发送 HTTP 请求
npm install axios --save
-
后端模拟接口 :准备两个后端服务(模拟不同端口的接口)
- 端口 5000:提供学生数据接口 :http://localhost:5000/students
- 端口 5001:提供汽车数据接口 :http://localhost:5000/cars
三、完整代码实现
1. 核心配置:vue.config.js(代理服务器配置)
Vue CLI 的配置文件,用于配置代理、关闭语法检查等,修改后需重启 Vue 项目才生效。
javascript
// common.js写法
module.exports = {
pages: {
index: {
entry: 'src/main.js',
},
},
// 关闭语法检查。
lintOnSave:false,
/*
配置代理服务器(方式一),需要重启Vue
devServer: {
proxy: 'http://localhost:5000'
},
*/
//配置代理服务器(方式二),需要重启Vue
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
pathRewrite:{'^/api':''},//表示发请求后将/api变为空
ws: true,//使用websoked。默认true
changeOrigin: true//告诉5000服务器请求来自5000,false表示告诉5000服务器(骗)来自8080。默认true
},
'/car': {
target: 'http://localhost:5001',
pathRewrite:{'^/car':''},
},
}
}
}
2. 入口文件:src/main.js
javascript
import Vue from 'vue'
//引入App组件
import App from './App.vue'
//关闭生产提示
Vue.config.productionTip = false
//创建vm
new Vue({
//将App组件放入容器(帮你挂到public/index.html)
render: h => h(App),
}).$mount('#app')//挂载。和配置el选项一样
3. 核心组件:src/App.vue(请求逻辑 + 页面渲染)
实现按钮点击请求数据、表格渲染,以及 Axios 请求的核心逻辑。
html
<template>
<div>
<input type="button" value="获取学生数据" @click="getStudents"/><br><br>
<table border="1px solid black">
<tr>
<td>id</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr v-for="student in students" :key="student.id">
<td>{{student.id}}</td>
<td>{{student.name}}</td>
<td>{{student.age}}</td>
</tr>
</table>
<hr>
<input type="button" value="获取汽车数据" @click="getCars"/><br><br>
<table border="1px solid black">
<tr>
<td>id</td>
<td>名称</td>
<td>价格</td>
</tr>
<tr v-for="car in cars" :key="car.id">
<td>{{car.id}}</td>
<td>{{car.name}}</td>
<td>{{car.price}}</td>
</tr>
</table>
</div>
</template>
<script>
//npm install axios
import axios from 'axios'
export default {
name:'App',
data() {
return {
students:[],
cars:[]
}
},
components:{
},
methods:{
/*
遇到跨域问题:浏览器使用XMLHttpRequest访问服务器时有同源策略限制
解决跨域问题:开启代理服务器(端口也是8080)。让代理服务器去和5000服务器拿数据。服务器之间数据交互直接使用http。没有同源限制
*/
getStudents(){
/*
配置代理服务器(方式一)
1 开启代理服务器后这里的请求端口直接写8080。
http://localhost:8080/students后面会解析成http://localhost:5000/students(只换端口)
2 若8080本身就有http://localhost:8080/students资源(例如public目录下有一个students)。就不会走代理服务器
3 不能配置多个代理
代码如下:
axios.get('http://localhost:8080/students').then(
response =>{
this.students = response.data
},
error =>{
console.log(error.message)
}
)
*/
/*
配置代理服务器(方式二)
1 可以解决第一种配置方法的第二,三点注意事项。
2 配置pathRewrite:{'^/api':''}后。最终发送请求为http://localhost:5000/students
*/
axios.get('http://localhost:8080/api/students').then(
response =>{
console.log(response.data)
this.students = response.data //直接返回对象数组了
},
error =>{
console.log(error.message)
}
)
},
getCars(){
axios.get('http://localhost:8080/car/cars').then(
response =>{
this.cars = response.data
},
error =>{
console.log(error.message)
}
)
}
}
}
</script>
<style>
</style>
四、知识点
1. 代理的核心原理
- 前端(8080)→ 代理服务器(8080,和前端同域)→ 后端接口(5000/5001)
- 由于代理服务器和前端同域,不存在跨域问题;而服务器之间的请求(代理→后端)不受浏览器同源策略限制,因此实现跨域请求。
2. 两种代理配置方式对比
配置方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
方式一(单代理) | 仅需转发到一个后端地址(如仅 5000 端口) | 配置简单,一行代码 | 无法同时转发到多个后端;若前端有同名接口(如 /public/students),会优先访问前端资源,不走代理 |
方式二(多代理) | 需转发到多个后端地址(如 5000 和 5001) | 支持多代理;可通过pathRewrite 避免前端资源冲突 |
配置相对复杂 |
方式一配置代码(参考注释)
javascript
// vue.config.js 方式一配置
devServer: {
proxy: 'http://localhost:5000' // 所有请求都转发到5000端口
}
使用时,请求地址直接写http://localhost:8080/students
,代理会自动转发到http://localhost:5000/students
。
3. 关键配置参数说明
参数 | 作用 | 取值 |
---|---|---|
target |
后端接口的基础地址(真实要转发的地址) | 如http://localhost:5000 |
pathRewrite |
路径重写(核心!) | 例如{'^/api': ''} :将请求中的/api 前缀替换为空,避免后端接收多余的/api |
ws |
是否支持 WebSocket(如实时通信) | 默认true ,无需修改 |
changeOrigin |
是否 "欺骗" 后端请求来源 | true :告诉后端 "请求来自target 地址(5000)";false :告诉后端真实来源(8080);默认true ,建议保持默认 |
五、常见问题与注意事项
-
配置后请求仍失败?
- 必须 重启 Vue 项目 !代理配置修改后不会热更新,需重新运行
npm run serve
。
- 必须 重启 Vue 项目 !代理配置修改后不会热更新,需重新运行
-
pathRewrite
为什么必须加?- 若不加,请求
/api/students
会被转发到http://localhost:5000/api/students
,若后端实际接口是/students
,导致 404 错误。 - 加上
pathRewrite: {'^/api': ''}
后,请求会被正确转发到http://localhost:5000/students
。
- 若不加,请求
-
如何验证代理是否生效?
- 打开浏览器 F12→Network→查看请求的
Request URL
是http://localhost:8080/api/students
(前端端口),而非 5000,说明代理已生效。
- 打开浏览器 F12→Network→查看请求的
-
前端资源和后端接口同名冲突?
- 例如前端
public
目录下有students资源
,后端有/students
接口:- 方式一代理会优先访问前端的
students
,不走代理; - 方式二通过
/api
前缀区分,可避免冲突(请求/api/students
走代理,请求/students
访问前端资源)。
- 方式一代理会优先访问前端的
- 例如前端