前端跨域以及解决方案
什么是跨域?
跨域指的是浏览器出于安全考虑,实施了同源策略。它限制了从一个源加载的文档或脚本如何与另一个源的资源进行交互。
同源策略规定:如果两个 URL 的协议(Protocol)、域名(Host)、端口(Port) 完全一致,则这两个 URL 是同源的。
只要其中任何一项不一致,就是跨域(或称不同源)。浏览器会对这种跨域的请求或交互进行限制。
举个例子:
| 当前页面URL | 请求的URL | 是否同源 | 原因 |
| :— | :— | :—: | :— |
| https://www.example.com/index.html
| https://www.example.com/api/user
| 是 | 协议、域名、端口完全相同 |
| https://www.example.com/index.html
| http://www.example.com/api/user
| 否 | 协议不同(HTTPS vs HTTP) |
| https://www.example.com/index.html
| https://api.example.com/user
| 否 | 域名不同(www vs api) |
| https://www.example.com:8080/index.html
| https://www.example.com:443/api/user
| 否 | 端口不同(8080 vs 443) |
常见的跨域场景表现:
- 前端域名
www.abc.com
,调用后端接口api.abc.com
。 - 本地开发
localhost:3000
,请求后端服务器localhost:8000
。 - 前端部署在
http
协议下,请求https
的接口。
常见的解决方案有哪些?
解决跨域的本质是“绕过”浏览器的同源策略限制。以下是几种主流且常用的方案,我会从最推荐的开始说起。
1. CORS(跨域资源共享) - 最主流、最推荐
这是 W3C 标准,属于跨域 Ajax 请求的根本解决方案。它需要后端服务器在响应头中设置特定的字段来告诉浏览器允许某个源进行跨域访问。
- 简单请求:不会触发预检请求。服务器需要设置:
Access-Control-Allow-Origin: https://www.example.com // 或 ‘*‘(允许任何源)
- 非简单请求(如使用了 PUT、DELETE 或自定义头):会先发送一个
OPTIONS
预检请求。服务器需要处理这个预检请求并返回:Access-Control-Allow-Origin: https://www.example.com Access-Control-Allow-Methods: GET, POST, PUT // 允许的方法 Access-Control-Allow-Headers: Content-Type, Authorization // 允许的头部
优点:安全、规范,是官方标准。
缺点:需要后端配合支持。
2. 反向代理(开发阶段常用)
在开发环境下,前端开发服务器(如 Webpack-dev-server, Vite)本身就提供了代理功能。其原理是:让同源的服务器去代理请求不同源的接口。
因为同源策略是浏览器的限制,服务器之间通信没有这个限制。
- 配置示例(Vite):
这样,前端请求// vite.config.js export default {server: {proxy: {'/api': { // 以 '/api' 开头的请求被代理target: 'http://localhost:8000', // 后端服务器地址changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, '') // 重写路径(可选)}}} }
/api/users
会被开发服务器代理到http://localhost:8000/users
。
优点:前端开发无感知,无需后端修改 CORS 配置,非常适合开发调试。
缺点:生产环境部署时需要配合 Nginx 等服务器做同样配置。
3. JSONP(已逐渐淘汰)
利用 <script>
标签没有跨域限制的特点来实现的。它只能用于 GET 请求。
- 步骤:
- 前端定义一个全局回调函数,例如
handleResponse
。 - 动态创建一个
script
标签,其src
指向请求的地址并带上回调函数名,如https://api.example.com/data?callback=handleResponse
。 - 后端接收请求后,返回一段 JavaScript 代码,代码内容是调用这个回调函数并传入数据:
handleResponse({...data...})
。 - 浏览器接收到响应后执行这段代码,前端定义的回调函数就会被调用并接收到数据。
- 前端定义一个全局回调函数,例如
优点:兼容性极好,支持老版本浏览器。
缺点:只支持 GET 方法,安全性差,容易遭受 XSS 攻击。现在基本已被 CORS 取代。
4. 其他方案(了解即可)
- WebSocket:WebSocket 协议本身允许跨域通信。
- postMessage:用于不同窗口(如 iframe、弹窗)之间的跨域通信,例如页面与内嵌 iframe 之间的消息传递。
- Nginx 反向代理:与开发环境代理原理相同,在生产环境中通过 Nginx 配置代理路径,将特定请求转发到真实的后端服务器。
location /api/ {proxy_pass http://backend-server.com:8000/; }
- 修改浏览器启动参数(仅测试用):启动 Chrome 时加上
--disable-web-security
参数来完全禁用同源策略(极度不安全,严禁在生产或开发中使用,仅用于临时测试)。
总结与面试回答建议
在面试中,你可以这样总结:
“跨域是由于浏览器的同源策略导致的安全限制。目前最主流和推荐的解决方案是 CORS,它需要后端配合设置响应头。在开发阶段,我们通常会使用反向代理(如 Webpack 或 Vite 的 proxy 配置)来无缝解决这个问题,因为这样前端代码无需任何改动。对于一些历史项目或特殊场景,可能会用到 JSONP,但它只支持 GET 且不安全,现在已不常用了。”
这样的回答表明你:
- 理解原理:清楚地解释了同源策略。
- 掌握方案:列出了多种解决方案并知道其优缺点。
- 具备工程化能力:知道在开发和生产环境下如何选择最合适的方案(开发用代理,生产用 CORS 或 Nginx)。
- 了解技术演进:知道 JSONP 的衰落和 CORS 的兴起。