Interview AiBoxInterview AiBox 实时 AI 助手,让你自信应答每一场面试
什么是跨域?有哪些解决跨域的方法?
题型摘要
跨域是Web开发中因浏览器同源策略导致的限制,当协议、域名或端口不同时发生。解决跨域的主要方法有:1) CORS(跨域资源共享),通过服务器设置HTTP响应头实现,是最推荐的标准化方案;2) JSONP,利用script标签跨域特性,但仅支持GET请求;3) 代理服务器,通过同源服务器转发请求;4) WebSocket,双向通信协议,不受同源限制;5) postMessage,HTML5 API,用于窗口间安全通信;6) document.domain,适用于子域间通信;7) window.name和location.hash,利用浏览器特性实现但安全性较低。选择方案需考虑安全性、兼容性、通信类型和实现复杂度等因素。
什么是跨域?有哪些解决跨域的方法?
跨域的定义
跨域(Cross-Origin)指的是在Web开发中,一个域下的文档或脚本试图去请求另一个域下的资源时,由于浏览器的同源策略(Same-Origin Policy)而产生的限制。
同源策略
同源策略是浏览器的一种安全机制,它阻止一个域的JavaScript脚本与另一个域的资源进行交互。所谓同源是指:
- 协议相同(如http、https)
- 域名相同(如example.com)
- 端口相同(如80、443)
只要这三者中有一个不同,就构成了跨域。
跨域的场景
跨域通常发生在以下场景:
- AJAX请求:使用XMLHttpRequest或Fetch API请求不同源的资源
- Cookie/LocalStorage访问:尝试访问不同源的Cookie或LocalStorage
- DOM访问:尝试通过JavaScript访问不同源页面的DOM元素
- 资源引用:如引用不同源的图片、CSS、脚本等(这些通常被允许,但有一些限制)
解决跨域的方法
1. CORS(跨域资源共享)
CORS(Cross-Origin Resource Sharing)是W3C标准,是最常用的跨域解决方案。它通过服务器设置HTTP响应头,告知浏览器哪些域可以访问资源。
实现方式
服务器端设置响应头:
// Node.js Express示例
app.use((req, res, next) => {
// 允许所有来源
res.header('Access-Control-Allow-Origin', '*');
// 允许的请求方法
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 允许的请求头
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// 允许发送Cookie
res.header('Access-Control-Allow-Credentials', 'true');
// 预检请求的缓存时间
res.header('Access-Control-Max-Age', '86400');
next();
});
客户端发送请求:
// 普通请求
fetch('http://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
// 带Cookie的请求
fetch('http://api.example.com/data', {
credentials: 'include'
});
优点
- 标准化解决方案,支持所有HTTP请求方法
- 服务器端控制,安全性高
- 支持Cookie和HTTP认证
缺点
- 需要服务器端配合
- 对于旧版浏览器支持有限
2. JSONP(JSON with Padding)
JSONP是一种非官方的跨域解决方案,利用了<script>标签的跨域特性。
实现方式
客户端:
// 创建script标签
function jsonp(url, callback) {
const script = document.createElement('script');
const callbackName = 'jsonp_callback_' + Date.now();
// 将回调函数挂载到window对象上
window[callbackName] = function(data) {
delete window[callbackName];
document.body.removeChild(script);
callback(data);
};
// 构建请求URL
script.src = `${url}?callback=${callbackName}`;
document.body.appendChild(script);
}
// 使用示例
jsonp('http://api.example.com/data', function(data) {
console.log(data);
});
服务器端:
// Node.js示例
app.get('/data', (req, res) => {
const callback = req.query.callback;
const data = { name: 'example', value: 123 };
// 返回包裹在回调函数中的JSON数据
res.send(`${callback}(${JSON.stringify(data)})`);
});
优点
- 兼容性好,支持旧版浏览器
- 实现简单
缺点
- 只支持GET请求
- 安全性较低,容易受到XSS攻击
- 服务器端需要特殊处理
- 错误处理困难
3. 代理服务器
通过同源服务器作为代理,将跨域请求转发到目标服务器。
实现方式
开发环境代理(以webpack-dev-server为例):
// webpack.config.js
module.exports = {
// ...其他配置
devServer: {
proxy: {
'/api': {
target: 'http://api.example.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
生产环境代理(以Nginx为例):
server {
listen 80;
server_name yourdomain.com;
location /api/ {
proxy_pass http://api.example.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
优点
- 对前端透明,无需修改前端代码
- 可以隐藏后端服务真实地址,增加安全性
- 可以统一处理请求头、Cookie等
缺点
- 需要额外的服务器资源
- 增加了请求的延迟
- 需要服务器配置权限
4. WebSocket
WebSocket是一种双向通信协议,它不受同源策略限制,可以用于跨域通信。
实现方式
客户端:
// 创建WebSocket连接
const socket = new WebSocket('ws://api.example.com/ws');
// 连接打开
socket.onopen = function(event) {
console.log('连接已建立');
// 发送消息
socket.send(JSON.stringify({ type: 'request', data: 'hello' }));
};
// 接收消息
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
// 连接关闭
socket.onclose = function(event) {
console.log('连接已关闭');
};
// 错误处理
socket.onerror = function(error) {
console.error('WebSocket错误:', error);
};
服务器端(Node.js示例):
const WebSocket = require('ws');
// 创建WebSocket服务器
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
console.log('客户端已连接');
// 接收消息
ws.on('message', function incoming(message) {
console.log('收到消息:', message);
// 发送消息
ws.send(JSON.stringify({ type: 'response', data: 'hello back' }));
});
// 连接关闭
ws.on('close', function() {
console.log('客户端已断开连接');
});
});
优点
- 支持双向实时通信
- 不受同源策略限制
- 通信效率高
缺点
- 需要专门的WebSocket服务器
- 协议与HTTP不同,需要特殊处理
- 连接状态管理复杂
5. postMessage
postMessage是HTML5引入的API,允许不同窗口(包括iframe)之间进行安全通信。
实现方式
发送消息:
// 父窗口向iframe发送消息
const iframe = document.getElementById('my-iframe').contentWindow;
iframe.postMessage('Hello from parent', 'http://iframe-domain.com');
// 或者新窗口
const newWindow = window.open('http://example.com');
newWindow.postMessage('Hello from opener', 'http://example.com');
接收消息:
// 在iframe或新窗口中接收消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'http://parent-domain.com') {
return;
}
console.log('收到消息:', event.data);
// 回复消息
event.source.postMessage('Hello from iframe', event.origin);
});
优点
- 安全性高,可以指定消息来源
- 支持不同窗口间的通信
- 可以传递各种类型的数据
缺点
- 需要双方都实现消息监听
- 只适用于窗口间通信,不适用于普通AJAX请求
6. document.domain
document.domain方法适用于子域之间的跨域通信,通过设置相同的domain值来实现。
实现方式
<!-- 在父页面 http://parent.example.com 中 -->
<script>
document.domain = 'example.com';
// 访问iframe
const iframe = document.getElementById('my-iframe');
iframe.onload = function() {
// 现在可以访问iframe的document
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
console.log(iframeDoc.body.innerHTML);
};
</script>
<!-- 在iframe页面 http://child.example.com 中 -->
<script>
document.domain = 'example.com';
</script>
优点
- 实现简单
- 适用于子域之间的通信
缺点
- 只适用于子域之间
- 安全性较低,容易受到攻击
- 现代浏览器对此有限制
7. window.name
window.name方法利用window对象在不同页面加载时保持name值的特性来实现跨域通信。
实现方式
<!-- 在页面A中 -->
<script>
// 创建一个隐藏的iframe
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
// 设置iframe的src为跨域页面
iframe.src = 'http://other-domain.com/data.html';
// iframe加载完成后,读取window.name
iframe.onload = function() {
// 读取数据
const data = iframe.contentWindow.name;
console.log('跨域数据:', data);
// 清理
iframe.onload = null;
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
};
document.body.appendChild(iframe);
</script>
<!-- 在跨域页面 http://other-domain.com/data.html 中 -->
<script>
// 将数据存储在window.name中
window.name = JSON.stringify({ key: 'value', data: [1, 2, 3] });
</script>
优点
- 可以传递较大数据(window.name支持约2MB数据)
- 实现相对简单
缺点
- 数据暴露在window.name中,安全性较低
- 只适用于单向通信
- 需要借助iframe等元素
8. location.hash
location.hash方法利用URL的hash部分(#后面的内容)在不同域之间传递数据。
实现方式
<!-- 在父页面 http://parent.com 中 -->
<iframe id="myFrame" src="http://child.com/#data" style="display:none;"></iframe>
<script>
// 监听hash变化
window.addEventListener('hashchange', function() {
const data = location.hash.substring(1);
console.log('收到数据:', data);
});
// 向iframe发送数据
function sendDataToChild(data) {
const iframe = document.getElementById('myFrame');
iframe.src = 'http://child.com/#' + encodeURIComponent(data);
}
</script>
<!-- 在iframe页面 http://child.com 中 -->
<script>
// 获取父页面传递的数据
const data = decodeURIComponent(location.hash.substring(1));
console.log('收到数据:', data);
// 向父页面发送数据
function sendDataToParent(data) {
parent.location.href = 'http://parent.com/#' + encodeURIComponent(data);
}
</script>
优点
- 实现简单
- 不需要服务器支持
缺点
- 数据直接暴露在URL中,安全性低
- 数据大小有限(URL长度限制)
- 会有历史记录,可能影响用户体验
跨域解决方案对比
如何选择合适的跨域解决方案
选择合适的跨域解决方案需要考虑以下因素:
- 安全性要求:对于敏感数据,应优先选择CORS或postMessage等安全性较高的方案
- 兼容性需求:如果需要支持旧版浏览器,可以考虑JSONP
- 通信类型:双向实时通信选择WebSocket,单向请求选择CORS或代理
- 开发环境:开发阶段可以使用代理服务器,生产环境可以使用CORS
- 性能要求:对于高频请求,WebSocket性能更好
- 实现复杂度:简单场景可以使用JSONP或location.hash,复杂场景建议使用CORS
总结
跨域是Web开发中的常见问题,由浏览器的同源策略引起。解决跨域的方法有多种,包括CORS、JSONP、代理服务器、WebSocket、postMessage等。每种方法都有其适用场景和优缺点,开发者需要根据具体需求选择合适的解决方案。在现代Web开发中,CORS是最常用和推荐的跨域解决方案,它提供了标准化的方式来安全地处理跨域请求。
思维导图
Interview AiBoxInterview AiBox — 面试搭档
不只是准备,更是实时陪练
Interview AiBox 在面试过程中提供实时屏幕提示、AI 模拟面试和智能复盘,让你每一次回答都更有信心。
AI 助读
一键发送到常用 AI
跨域是Web开发中因浏览器同源策略导致的限制,当协议、域名或端口不同时发生。解决跨域的主要方法有:1) CORS(跨域资源共享),通过服务器设置HTTP响应头实现,是最推荐的标准化方案;2) JSONP,利用script标签跨域特性,但仅支持GET请求;3) 代理服务器,通过同源服务器转发请求;4) WebSocket,双向通信协议,不受同源限制;5) postMessage,HTML5 API,用于窗口间安全通信;6) document.domain,适用于子域间通信;7) window.name和location.hash,利用浏览器特性实现但安全性较低。选择方案需考虑安全性、兼容性、通信类型和实现复杂度等因素。
智能总结
深度解读
考点定位
思路启发
相关题目
请做一个自我介绍
自我介绍是面试的开场环节,应遵循"三段式"结构:基本信息与教育背景、核心能力与项目经验、求职动机与个人特质。重点突出与岗位相关的技能和经验,用具体数据和成果支撑,保持真诚自然的表达,控制在2-3分钟内。针对不同公司和岗位进行个性化调整,展示自己的匹配度和价值。
你有什么问题想问我们公司或团队的吗?
面试结尾提问是展示面试者思考深度和职业素养的重要机会。应提前准备3-5个有深度的问题,围绕团队技术、个人成长、公司文化和业务发展四个方面。好的问题能体现你对公司的了解、对职位的重视以及你的职业规划,避免问基础信息类问题。
请做一个自我介绍
自我介绍应遵循“我是谁-我为什么能胜任-我为什么想来”的逻辑框架。在“能胜任”部分,要通过STAR法则和量化结果来突出技术亮点和项目经验。在“想来”部分,要表达对华为技术、文化或业务的认同,展现匹配度和诚意。整个过程应简洁有力,控制在1-3分钟内。
请做一个自我介绍
自我介绍是面试的开场环节,应简洁明了地展示个人基本信息、教育背景、项目经验、技术特长、个人特质和求职动机。优秀的自我介绍应结构清晰、重点突出,与应聘岗位高度匹配,并表达出对公司的了解和加入的强烈意愿。
请做一个自我介绍,包括你的技术背景、项目经验和学习方向。
自我介绍应包含四个核心部分:个人背景、技术能力、项目经验和学习规划。技术背景需突出前端技术栈掌握程度;项目经验应选择代表性案例,说明技术实现和个人贡献;学习方向要体现职业规划与公司发展的契合度。整体表达应简洁有力,重点突出,时间控制在3-5分钟内。