在前端面试里,跨域 一直是高频考点。无论是 Vue、React 项目,还是 Node.js 后端,只要涉及到 前后端分离 ,跨域几乎是必然要遇到的问题。很多同学知道要加 Access-Control-Allow-Origin
,却说不清楚为什么会跨域、有哪些解决办法,以及这些方案的优缺点。
这篇文章就从 同源策略 开始,系统讲清楚跨域的来龙去脉,并结合代码案例说明常见的几种跨域解决方案,最后还会给出 面试回答的套路,帮助你在面试时说得有理有据。
一、跨域的根源:同源策略
同源策略(Same-Origin Policy, SOP) 是浏览器最核心的安全机制之一。
它规定:只有协议、域名、端口三者完全一致时,才能互相访问数据。
-
同源
http://localhost:5173
http://localhost:5173/api/user
✅ 同源(协议/域名/端口完全一致)
-
不同源
http://localhost:5173
❌http://localhost:8080/api/user
(端口不同)http://localhost:5173
❌https://localhost:5173/api/user
(协议不同)http://localhost:5173
❌http://www.baidu.com/api
(域名不同)
同源策略的目的很简单:保护用户数据安全,避免恶意网站窃取隐私信息 。
比如:你已经登录了 bank.com
,恶意网站 evil.com
想用你的登录状态去请求银行接口,拿到余额信息。如果没有同源策略,风险就非常大。
👉 面试回答建议
"跨域问题的根源是浏览器的同源策略,它是浏览器端的安全机制,用来阻止不同源的网页任意访问数据,从而避免隐私和安全风险。"
二、跨域解决方案
跨域问题是浏览器的限制,所以"解题思路"只有两类:
- 绕开浏览器限制(比如 JSONP、代理、WebSocket);
- 告诉浏览器这是安全的(比如 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(跨域资源共享) 是现在最常用的跨域方案,浏览器和服务器都支持。
工作原理:
- 浏览器请求时自动带上
Origin
请求头,告诉后端本次请求的来源。 - 后端通过
Access-Control-Allow-Origin
响应头决定是否放行。
-
简单请求 (GET/POST/HEAD 且 Content-Type 为普通类型):
只需返回:
jsres.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
-
复杂请求 (PATCH/DELETE/自定义头等):
浏览器会先发一个 OPTIONS 预检请求 ,确认允许后再发真正请求。
后端需要额外设置:
jsres.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
,开发服务器帮你转发请求,浏览器认为请求的是同源。jsserver: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } } }
-
上线阶段(反向代理) :Nginx 拦截前端请求并转发到后端。
jslocation /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 之间传递数据,或者第三方登录授权场景。"
三、面试答题思路
面试官问:"跨域怎么解决?"
可以用这个逻辑回答,既条理清晰,又能展现深度:
-
先讲本质
"跨域问题是浏览器同源策略导致的。"
-
列举常见方案
"常见方案有 JSONP、CORS、代理、WebSocket、postMessage。"
-
点明主流方案
"实际项目里,CORS 是最常用的解决方式,通过后端设置响应头实现。"
-
结合场景经验
"开发环境我会用 vite 的代理解决,上线后用 nginx 反向代理;如果是即时通信,就直接用 WebSocket。"
这样不仅展示了知识点,还能让面试官觉得你真的做过项目。
四、总结
跨域并不神秘,关键是要理解:
- 本质:同源策略的安全限制;
- 方案:JSONP(历史)、CORS(主流)、代理(常用)、WebSocket(实时)、postMessage(窗口通信);
- 场景:结合项目经验灵活选择。
面试时,按照 "原因 → 方案 → 实际应用" 的顺序来答,清晰又有深度,几乎可以秒杀这一题。