前端跨域详解:从原理到实战解决方案

0 阅读5分钟

前端跨域详解:从原理到实战解决方案

在前端开发中,跨域是几乎每个项目都会遇到的问题,也是面试中的高频考点。很多初学者对跨域一知半解,只知道会报错却不明白根源,更不清楚不同场景下该用哪种方案。本文就从零开始,带你彻底搞懂跨域 ——为什么会跨域、浏览器做了什么、开发 / 生产环境分别怎么解决,内容通俗易懂,适合新手学习与面试复习。


一、什么是跨域?为什么会出现跨域?

1. 同源策略(Same-Origin Policy)

跨域的根源,是浏览器遵守的同源策略

所谓 “同源”,要求 URL 的三个部分完全一致:

  1. 协议(http / https)
  2. 域名(主域名 / 子域名均算)
  3. 端口号(: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. 原理

浏览器只认当前页面所在的域,我们让本地开发服务器做一层转发:

  1. 前端 → 本地开发服务器(同源,不跨域)
  2. 本地服务器 → 后端接口(服务器之间不存在跨域限制)

浏览器以为是同域请求,就不会拦截。

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)简单请求

直接发送,不会触发预检。必须同时满足:

  • 请求方法:仅限 GETPOSTHEAD

  • 请求头仅限:

    • Accept
    • Accept-Language
    • Content-Language
  • Content-Type 仅限:

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 无自定义请求头(如 token、Authorization)

(2)复杂请求

不满足简单请求条件即为复杂请求,例如:

  • 使用 PUTDELETEPATCH
  • 自定义请求头(token、Content-Type: application/json)
  • 携带 Cookie

复杂请求会触发预检(Preflight) :浏览器会自动先发一个 OPTIONS 请求,向后端确认两件事:

  1. 允许当前域名访问吗?
  2. 允许当前请求方法和请求头吗?

后端通过后,浏览器才会发送真正的业务请求。

3. 后端关键 CORS 响应头

  • Access-Control-Allow-Origin:允许的域名(* 或具体域名)
  • Access-Control-Allow-Methods:允许的请求方法
  • Access-Control-Allow-Headers:允许的自定义头
  • Access-Control-Allow-Credentials:是否允许携带 Cookie
  • Access-Control-Max-Age:预检结果缓存时间,减少重复 OPTIONS

4. 重要坑点

如果开启了:

plaintext

Access-Control-Allow-Credentials: true

那么 Access-Control-Allow-Origin 不能为 * ,必须配置具体域名,否则浏览器直接报错。


四、经典老方案:JSONP

JSONP 是早年兼容低版本浏览器的跨域方案,现在基本不用,但面试常问

1. 原理

浏览器的同源策略只限制 Ajax/fetch,但不限制 <script src="..."> 加载外部 JS。JSONP 就是利用这一点实现跨域:

  1. 前端定义一个全局回调函数;
  2. 动态创建 <script> 标签,地址带上回调名;
  3. 后端返回一段 “函数调用” 的 JS 代码;
  4. 脚本加载后自动执行,前端拿到数据。

2. 特点

  • 优点:兼容性极好,支持老 IE

  • 缺点:

    • 只支持 GET 请求
    • 无法优雅捕获错误
    • 存在 XSS 安全风险
    • 现代项目基本废弃

五、其他常用跨域方案

1. Nginx 反向代理

线上部署常用,思路和本地 Proxy 完全一致:

  • 浏览器 → Nginx(同域)
  • Nginx → 后端服务

通过 Nginx 配置路径转发,前端完全感知不到跨域。

2. postMessage

用于iframe 与父页面之间的跨域通信。通过 window.postMessage 发送数据,并在接收方校验 origin 保证安全。


六、总结:跨域核心要点

  1. 跨域由浏览器同源策略引起,目的是安全;
  2. 跨域时请求已发出,只是浏览器拦截了响应;
  3. 开发环境:Vite/Webpack Proxy 代理
  4. 生产环境:后端配置 CORSNginx 反向代理
  5. 复杂请求会触发 OPTIONS 预检;
  6. 带 Cookie 跨域时,Origin 不能为 *,且前后端都要开启 credentials;
  7. JSONP 仅用于面试,实际开发优先 CORS。