面试必问:跨域问题的原理与解决方案

在前端面试里,跨域 一直是高频考点。无论是 Vue、React 项目,还是 Node.js 后端,只要涉及到 前后端分离 ,跨域几乎是必然要遇到的问题。很多同学知道要加 Access-Control-Allow-Origin,却说不清楚为什么会跨域、有哪些解决办法,以及这些方案的优缺点。

这篇文章就从 同源策略 开始,系统讲清楚跨域的来龙去脉,并结合代码案例说明常见的几种跨域解决方案,最后还会给出 面试回答的套路,帮助你在面试时说得有理有据。


一、跨域的根源:同源策略

同源策略(Same-Origin Policy, SOP) 是浏览器最核心的安全机制之一。

它规定:只有协议、域名、端口三者完全一致时,才能互相访问数据。

  • 同源

    • http://localhost:5173
    • http://localhost:5173/api/user ✅ 同源(协议/域名/端口完全一致)
  • 不同源

    • http://localhost:5173http://localhost:8080/api/user (端口不同)
    • http://localhost:5173https://localhost:5173/api/user (协议不同)
    • http://localhost:5173http://www.baidu.com/api (域名不同)

同源策略的目的很简单:保护用户数据安全,避免恶意网站窃取隐私信息

比如:你已经登录了 bank.com,恶意网站 evil.com 想用你的登录状态去请求银行接口,拿到余额信息。如果没有同源策略,风险就非常大。

👉 面试回答建议

"跨域问题的根源是浏览器的同源策略,它是浏览器端的安全机制,用来阻止不同源的网页任意访问数据,从而避免隐私和安全风险。"


二、跨域解决方案

跨域问题是浏览器的限制,所以"解题思路"只有两类:

  1. 绕开浏览器限制(比如 JSONP、代理、WebSocket);
  2. 告诉浏览器这是安全的(比如 CORS、postMessage)。

下面逐个拆解。


1. JSONP ------ script 天然跨域

在早期,最常用的跨域方式是 JSONP(JSON with Padding) 。它利用了 <script> 标签可以跨域加载资源的特性。

实现原理:

  • 前端创建一个 <script> 标签,请求跨域接口,并带上一个 callback 参数;
  • 后端返回一段 JS 代码,执行这个 callback,把数据作为参数传进去;
  • 前端事先定义好 callback,就能拿到数据。

前端封装:

js 复制代码
function getJSONP({ url, params = {}, callback }) {
  return new Promise((resolve) => {
    const script = document.createElement('script');
    params = { ...params, callback };
    const query = Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&');
    script.src = `${url}?${query}`;
    window[callback] = (data) => resolve(data);
    document.body.appendChild(script);
  });
}

// 使用
getJSONP({
  url: 'http://localhost:3000/say',
  params: { wd: 'I love you' },
  callback: 'show'
}).then((data) => console.log(data));

后端返回:

js 复制代码
if (req.url.startsWith('/say')) {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const callback = url.searchParams.get('callback');
  const data = { msg: '我不爱你' };

  res.writeHead(200, { 'Content-Type': 'application/javascript' });
  res.end(`${callback}(${JSON.stringify(data)})`);
}

优缺点:

  • ✅ 兼容性好,几乎所有浏览器支持;
  • ❌ 只能 GET 请求;
  • ❌ 不安全,全局挂载 callback,容易被劫持。

👉 面试回答建议

"JSONP 是早期跨域方案,利用 script 标签可以跨域的特性,只能用 GET 请求,且需要后端配合,现在实际项目里很少用。"


2. CORS ------ 主流方案

CORS(跨域资源共享) 是现在最常用的跨域方案,浏览器和服务器都支持。

工作原理:

  1. 浏览器请求时自动带上 Origin 请求头,告诉后端本次请求的来源。
  2. 后端通过 Access-Control-Allow-Origin 响应头决定是否放行。
  • 简单请求 (GET/POST/HEAD 且 Content-Type 为普通类型):

    只需返回:

    js 复制代码
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
  • 复杂请求 (PATCH/DELETE/自定义头等):

    浏览器会先发一个 OPTIONS 预检请求 ,确认允许后再发真正请求。

    后端需要额外设置:

    js 复制代码
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

示例后端:

js 复制代码
const server = http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH,OPTIONS');

  if (req.method === 'OPTIONS') {
    res.writeHead(200);
    res.end();
    return;
  }

  if (req.url === '/api/test' && req.method === 'PATCH') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ msg: '跨域成功!' }));
  }
});

优缺点:

  • ✅ 支持所有 HTTP 方法;
  • ✅ 配置简单,主流方案;
  • ❌ 需要后端支持,不是前端自己能解决的。

👉 面试回答建议

"CORS 是目前最常用的方案,浏览器会带上 Origin 头,后端在响应头设置 Access-Control-Allow-Origin 就能解决。复杂请求会触发预检 OPTIONS。"


3. 代理 ------ 开发和生产常用

  • 开发阶段(正向代理) :Vite 或 Webpack 配置 proxy,开发服务器帮你转发请求,浏览器认为请求的是同源。

    js 复制代码
    server: {
      proxy: {
        '/api': {
          target: 'http://localhost:3000',
          changeOrigin: true
        }
      }
    }
  • 上线阶段(反向代理) :Nginx 拦截前端请求并转发到后端。

    js 复制代码
    location /api/ {
      proxy_pass http://localhost:3000/;
    }

👉 面试回答建议

"开发环境用 dev-server 或 vite 的代理解决跨域,上线后一般用 nginx 做反向代理,把跨域请求转发到同源服务。"


4. WebSocket ------ 不受同源限制

WebSocket 是 HTML5 提供的双向通信协议,不受同源策略限制。前端和后端直接通过 ws://wss:// 建立连接。

前端:

js 复制代码
const ws = new WebSocket("ws://localhost:8080/ws");
ws.onopen = () => ws.send("Hello from client!");
ws.onmessage = (e) => console.log("收到:", e.data);

后端:

js 复制代码
const wss = new WebSocket.Server({ server, path: '/ws' });
wss.on('connection', (ws) => {
  ws.on('message', (msg) => ws.send(`Server received: ${msg}`));
  ws.send('欢迎来到 WebSocket 服务!');
});

👉 面试回答建议

"WebSocket 不受同源策略限制,常用于即时通信场景,比如聊天室、股票行情、协作编辑等。"


5. postMessage ------ 跨窗口通信

浏览器提供的 postMessage API,可以实现 iframe、子窗口和父窗口之间安全通信。

父窗口:

js 复制代码
const win = document.querySelector('.child-iframe').contentWindow;
win.postMessage('Hello child', 'http://127.0.0.1:5500');

子窗口:

js 复制代码
window.addEventListener('message', (e) => {
  if (e.origin !== 'http://127.0.0.1:5500') return;
  console.log('收到消息:', e.data);
});

👉 面试回答建议

"postMessage 是浏览器提供的跨窗口通信 API,常用于父页面和 iframe 之间传递数据,或者第三方登录授权场景。"


三、面试答题思路

面试官问:"跨域怎么解决?"

可以用这个逻辑回答,既条理清晰,又能展现深度:

  1. 先讲本质

    "跨域问题是浏览器同源策略导致的。"

  2. 列举常见方案

    "常见方案有 JSONP、CORS、代理、WebSocket、postMessage。"

  3. 点明主流方案

    "实际项目里,CORS 是最常用的解决方式,通过后端设置响应头实现。"

  4. 结合场景经验

    "开发环境我会用 vite 的代理解决,上线后用 nginx 反向代理;如果是即时通信,就直接用 WebSocket。"

这样不仅展示了知识点,还能让面试官觉得你真的做过项目。


四、总结

跨域并不神秘,关键是要理解:

  • 本质:同源策略的安全限制;
  • 方案:JSONP(历史)、CORS(主流)、代理(常用)、WebSocket(实时)、postMessage(窗口通信);
  • 场景:结合项目经验灵活选择。

面试时,按照 "原因 → 方案 → 实际应用" 的顺序来答,清晰又有深度,几乎可以秒杀这一题。

相关推荐
Cache技术分享3 小时前
194. Java 异常 - Java 异常处理之多重捕获
前端·后端
新酱爱学习3 小时前
🚀 Web 图片优化实践:通过 AVIF/WebP 将 12MB 图片降至 4MB
前端·性能优化·图片资源
用户916357440954 小时前
CSS中的"后"发制人
前端·css
小满xmlc4 小时前
react Diff 算法
前端
bug_kada4 小时前
Js 的事件循环(Event Loop)机制以及面试题讲解
前端·javascript
bug_kada4 小时前
深入理解 JavaScript 可选链操作符
前端·javascript
小满xmlc4 小时前
CI/CD 构建部署
前端
_AaronWong4 小时前
视频加载Loading指令:基于Element Plus的优雅封装
前端·electron
KallkaGo4 小时前
threejs复刻原神渲染(三)
前端·webgl·three.js