前端跨域详解:从原理到实战解决方案
在前端开发中,跨域是几乎每个项目都会遇到的问题,也是面试中的高频考点。很多初学者对跨域一知半解,只知道会报错却不明白根源,更不清楚不同场景下该用哪种方案。本文就从零开始,带你彻底搞懂跨域 ——为什么会跨域、浏览器做了什么、开发 / 生产环境分别怎么解决,内容通俗易懂,适合新手学习与面试复习。
一、什么是跨域?为什么会出现跨域?
1. 同源策略(Same-Origin Policy)
跨域的根源,是浏览器遵守的同源策略。
所谓 “同源”,要求 URL 的三个部分完全一致:
- 协议(http / https)
- 域名(主域名 / 子域名均算)
- 端口号(:3000 / :8080 / :80)
只要三者中有任意一个不同,就构成跨域。
2. 浏览器为什么要禁止跨域?
核心目的:保障用户安全。
举个很直观的例子:
- 你登录了银行网站,浏览器自动保存了登录 Cookie;
- 此时你不小心打开一个恶意网站;
- 如果没有跨域限制,这个网站可以直接请求银行接口,带上你的 Cookie 操作账户;
- 后果不堪设想。
所以浏览器强制限制跨域 Ajax 请求,本质是一种安全防护机制。
3. 一个常见误区:跨域是请求发不出去吗?
并不是。
- 请求已经成功发送到后端;
- 后端也正常接收、处理并返回了数据;
- 只是浏览器认为不安全,把响应结果拦截了,不交给前端 JS。
跨域报错通常长这样:
plaintext
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
二、开发环境最常用:Proxy 代理
本地开发时,前端运行在 localhost:3000,后端接口在 localhost:8080,端口不同必然跨域。最通用、最简单的方案就是:开发服务器代理(Proxy) 。
1. 原理
浏览器只认当前页面所在的域,我们让本地开发服务器做一层转发:
- 前端 → 本地开发服务器(同源,不跨域)
- 本地服务器 → 后端接口(服务器之间不存在跨域限制)
浏览器以为是同域请求,就不会拦截。
2. Vite 配置示例
js
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080', // 后端真实地址
changeOrigin: true
}
}
}
}
Webpack 配置思路完全一致,只是配置位置不同。
三、生产环境标准方案:CORS 跨域资源共享
CORS 是现代项目解决跨域的官方标准方案,由后端配置实现,前端几乎不用改代码。
1. 原理
后端在响应头中添加声明,告诉浏览器:
“这个来源是我允许的,你可以放行。”
只要后端正确配置 CORS,浏览器就不再拦截跨域请求。
2. CORS 中的两种请求
CORS 把请求分为两类,行为不同:
(1)简单请求
直接发送,不会触发预检。必须同时满足:
-
请求方法:仅限
GET、POST、HEAD -
请求头仅限:
- Accept
- Accept-Language
- Content-Language
-
Content-Type 仅限:
application/x-www-form-urlencodedmultipart/form-datatext/plain
-
无自定义请求头(如 token、Authorization)
(2)复杂请求
不满足简单请求条件即为复杂请求,例如:
- 使用
PUT、DELETE、PATCH - 自定义请求头(token、Content-Type: application/json)
- 携带 Cookie
复杂请求会触发预检(Preflight) :浏览器会自动先发一个 OPTIONS 请求,向后端确认两件事:
- 允许当前域名访问吗?
- 允许当前请求方法和请求头吗?
后端通过后,浏览器才会发送真正的业务请求。
3. 后端关键 CORS 响应头
Access-Control-Allow-Origin:允许的域名(* 或具体域名)Access-Control-Allow-Methods:允许的请求方法Access-Control-Allow-Headers:允许的自定义头Access-Control-Allow-Credentials:是否允许携带 CookieAccess-Control-Max-Age:预检结果缓存时间,减少重复 OPTIONS
4. 重要坑点
如果开启了:
plaintext
Access-Control-Allow-Credentials: true
那么 Access-Control-Allow-Origin 不能为 * ,必须配置具体域名,否则浏览器直接报错。
四、经典老方案:JSONP
JSONP 是早年兼容低版本浏览器的跨域方案,现在基本不用,但面试常问。
1. 原理
浏览器的同源策略只限制 Ajax/fetch,但不限制 <script src="..."> 加载外部 JS。JSONP 就是利用这一点实现跨域:
- 前端定义一个全局回调函数;
- 动态创建
<script>标签,地址带上回调名; - 后端返回一段 “函数调用” 的 JS 代码;
- 脚本加载后自动执行,前端拿到数据。
2. 特点
-
优点:兼容性极好,支持老 IE
-
缺点:
- 只支持 GET 请求
- 无法优雅捕获错误
- 存在 XSS 安全风险
- 现代项目基本废弃
五、其他常用跨域方案
1. Nginx 反向代理
线上部署常用,思路和本地 Proxy 完全一致:
- 浏览器 → Nginx(同域)
- Nginx → 后端服务
通过 Nginx 配置路径转发,前端完全感知不到跨域。
2. postMessage
用于iframe 与父页面之间的跨域通信。通过 window.postMessage 发送数据,并在接收方校验 origin 保证安全。
六、总结:跨域核心要点
- 跨域由浏览器同源策略引起,目的是安全;
- 跨域时请求已发出,只是浏览器拦截了响应;
- 开发环境:Vite/Webpack Proxy 代理;
- 生产环境:后端配置 CORS 或 Nginx 反向代理;
- 复杂请求会触发
OPTIONS预检; - 带 Cookie 跨域时,
Origin不能为 *,且前后端都要开启 credentials; - JSONP 仅用于面试,实际开发优先 CORS。